日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

rhel 8.2不识别unicode_Unicode的文本处理二三事

發(fā)布時(shí)間:2025/4/16 66 豆豆
生活随笔 收集整理的這篇文章主要介紹了 rhel 8.2不识别unicode_Unicode的文本处理二三事 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文不對(duì)unicode進(jìn)行科普式的宣講,主要針對(duì)其在文本處理過(guò)程中的一些有趣應(yīng)用進(jìn)行記錄和剖析。

0x01. 前言

在日常工作的文本處理過(guò)程中,經(jīng)常會(huì)遇到一些利用unicode特性對(duì)文本進(jìn)行處理的技巧。在這篇文章中則主要對(duì)其進(jìn)行一些匯總和剖析。在開(kāi)始之前,這里對(duì) unicode 一些鮮為人知的概念做一些介紹。

大部分時(shí)候,我們都會(huì)只認(rèn)為Unicode只是對(duì)字符的一個(gè)數(shù)字編碼,在Python內(nèi)部,我們可以通過(guò)這樣的方式,查看一個(gè)文本的 unicode 編碼:

a = "母" a.encode("raw_unicode_escape") # b'u2e9f'

但實(shí)際上,一個(gè)unicode除了其 codepoint 之外,還有很多特殊的屬性,而這些屬性在很多的NLP處理任務(wù)的過(guò)程中起到幫助的作用。如下圖:

unicode的其他屬性

在這里,我推薦使用這個(gè)站點(diǎn)來(lái)查詢unicode的相關(guān)屬性。從上圖可以看出,一個(gè)unicode還具備以下常用的屬性:

  • Name: 每個(gè)Unicode會(huì)有一個(gè)獨(dú)特的名字,后面我們會(huì)展示一個(gè)根據(jù)名字前綴來(lái)識(shí)別unicode屬于哪一種語(yǔ)言的技巧。
  • Block: 一個(gè)連續(xù)的編碼范圍,具體可以參考:Wikipedia - Unicode block
  • Plane: 具體可以參考:Wikipedia - Plane (Unicode)
  • Script: 每個(gè)文字的書(shū)寫(xiě)體系,具體可以參考:Wikipedia - Script(Unicode)。
  • Category: 類(lèi)別,待會(huì)會(huì)詳細(xì)介紹。

0x02. Unicode Range

我們都知道unicode利用一個(gè)數(shù)字來(lái)表示每個(gè)字符。而實(shí)際上,每個(gè)書(shū)寫(xiě)語(yǔ)言(script)所涉及的文字,都有其獨(dú)特的unicode范圍。因此最直接的一個(gè)應(yīng)用就是利用 unicode range 來(lái)判定一個(gè)字符 or 文本屬于哪一種語(yǔ)言。

在開(kāi)始之前,我先推薦一個(gè)站點(diǎn):Code Chars。這個(gè)站點(diǎn)按照不用的書(shū)寫(xiě)語(yǔ)言和地域進(jìn)行分類(lèi),列舉出每個(gè)語(yǔ)言的unicode range。如下圖綠框,其中中文script的名字叫做 Unihan。

Code Chars:不同語(yǔ)言的unicode-range

在上面站點(diǎn)可以查詢到,漢字(Han scirpt)包含以下的block,而每個(gè)block的 block-range 可以表示為:

  • CJK Unified Ideographs: U+4E00–U+9FEF
  • CJK Unified Ideographs Extension A: U+3400–U+4DB5
  • CJK Unified Ideographs Extension B: U+20000–U+2A6D6
  • CJK Unified Ideographs Extension C: U+2A700–U+2B734
  • CJK Unified Ideographs Extension D: U+2B740–U+2B81D
  • CJK Unified Ideographs Extension E: U+2B820–U+2CEA1
  • CJK Unified Ideographs Extension F: U+2CEB0–U+2EBE0

因此,我們可以根據(jù)上述的 unicode-range,開(kāi)開(kāi)心心的寫(xiě)一個(gè)判定是否為漢字的正則表達(dá)式

HAN_SCRIPT_PAT = re.compile(r'[u4E00-u9FEFu3400-u4DB5u20000-u2A6D6u2A700-u2B734'r'u2B740-u2B81Du2D820-u2CEA1u2CEB0-u2EBE0]' )def is_chinese_char(c):return bool(HAN_SCRIPT_PAT.match(c))

然而值得注意的是,這種方法并不算是一種很好的方式。因?yàn)椴煌淖值膗nicode范圍會(huì)有變化。如果只是一次性的搞一波,那也可以考慮一下。

0x03. Unicode 的其他屬性應(yīng)用

在這一小節(jié),我們主要討論unicode的其他屬性以及 normalize 的問(wèn)題,主要涉及 Python 中 unicodedata 和 regrex 兩個(gè)標(biāo)準(zhǔn)庫(kù)。

3.1 字符名字(Name)判斷:

在第一小節(jié)中我們提及到,每個(gè)unicode字符都有其獨(dú)特的名字。在Python中,我們可以通過(guò)這樣的方式來(lái)獲取某個(gè)unicode字符的名字:

import unicodedata text = "中" print(unicodedata.name(text)) # CJK UNIFIED IDEOGRAPH-4E2D

進(jìn)一步的,我們可以簡(jiǎn)單來(lái)看下多個(gè)unicode的名字特點(diǎn):從下表可以看到: 對(duì)于中文字符,其 Unicode 名字都是以 CJK 開(kāi)頭; 對(duì)于印地語(yǔ)(天成文),其前綴也基本是以 DEVANAGARI 開(kāi)頭; * 對(duì)于表情符號(hào),其名字還包含了表情符號(hào)本身的文字描述。這額外的描述也可以在NLP任務(wù)過(guò)程中作為表情符號(hào)的特征進(jìn)行補(bǔ)充,讓模型能夠更好的理解符號(hào)本身。

回到判定字符所屬的語(yǔ)言任務(wù)本身,利用Unicode-range判定法會(huì)存在范圍變化的問(wèn)題。那么可以更改為利用名字判斷:

def is_chinese_char(c):return unicodedata.name(c).startswith("CJK")

除了利用名字之外,更加規(guī)范的做法應(yīng)該是直接判斷該unicode的Script屬性(漢字的Script屬于Han)。可惜 unicodedata 這個(gè)庫(kù)不支持。但是可以用 regrex 庫(kù)搞一波:

def is_chinese_char(c):return bool(regrex.match(r"p{script=han}", c))

3.2 字符類(lèi)別(Category)判斷:

在Unicode中,每個(gè)字符還會(huì)被賦予上Category的屬性,而這個(gè)屬性跟語(yǔ)種是無(wú)關(guān)的。總體而言,Category一共分為 Letter, Mark, Number, Punctuation, Symbol, Seperator, Other 七大類(lèi), 而每個(gè)類(lèi)別下面還有進(jìn)一步的二級(jí)分類(lèi)。在 Python 中,我們可以利用 unicodedata.category 這個(gè)庫(kù)來(lái)獲取這個(gè)屬性;

import unicodedatarst = [] for char in "1a天。 ??":rst.append("{}:{}".format(char, unicodedata.category(char)))print(",".join(rst))# 1:Nd,a:Ll,天:Lo,。:Po, :So,?:So,?:Mn

更詳細(xì)的,我們可以來(lái)看看所有Category的類(lèi)型碼和對(duì)應(yīng)信息類(lèi)別:

二級(jí)Category列表,參考[1]

一旦知曉了字符的類(lèi)別,那么在文本處理過(guò)程中就有很多技巧可以應(yīng)用的上的。例如:

  • 利用類(lèi)別中P開(kāi)頭的字符,把標(biāo)點(diǎn)符號(hào)全部篩選出來(lái)。
  • 類(lèi)別N開(kāi)頭的是數(shù)字符號(hào),除了常見(jiàn)的阿拉伯?dāng)?shù)字,還可以將羅馬數(shù)字、其他語(yǔ)種的數(shù)字體、帶圓圈的數(shù)序序號(hào)等也排除出來(lái)。
unicodedata.category("?") == 'Nd' # 天成文中的數(shù)字2 unicodedata.category("⑩") == 'Nd'
  • 利用類(lèi)別中C類(lèi)別的字符,可以把文本中一些不可見(jiàn)的控制字符(如"^V, ^I" 或者zero-width的如u200d等字符)給過(guò)濾掉:
import unicodedata text = text.replace("t", " ") return "".join(ch for ch in text if unicodedata.category(ch)[0] != 'C')

在這里,我展示一下 tensor2tensor 中計(jì)算 BLEU 分?jǐn)?shù)的時(shí)候,用于分詞的函數(shù) bleu_tokenizer:

class UnicodeRegex(object):"""Ad-hoc hack to recognize all punctuation and symbols."""def __init__(self):# 獲取所有的標(biāo)點(diǎn)符號(hào)punctuation = self.property_chars("P")# 標(biāo)點(diǎn)符號(hào)左邊不帶數(shù)字self.nondigit_punct_re = re.compile(r"([^d])([" + punctuation + r"])")# 標(biāo)點(diǎn)符號(hào)右邊不帶數(shù)字self.punct_nondigit_re = re.compile(r"([" + punctuation + r"])([^d])")# 所有的符號(hào)集合self.symbol_re = re.compile("([" + self.property_chars("S") + "])")def property_chars(self, prefix):return "".join(six.unichr(x) for x in range(sys.maxunicode)if unicodedata.category(six.unichr(x)).startswith(prefix))uregex = UnicodeRegex()def bleu_tokenize(string):# 粗暴的分割所有除了前后包含數(shù)字的標(biāo)點(diǎn)符號(hào)。string = uregex.nondigit_punct_re.sub(r"1 2 ", string)string = uregex.punct_nondigit_re.sub(r" 1 2", string)# 所有的symbol默認(rèn)分割string = uregex.symbol_re.sub(r" 1 ", string)return string.split()

3.3 對(duì)unicode字符進(jìn)行normalized:

在某些自然語(yǔ)言處理任務(wù)的過(guò)程中,會(huì)遇到一些神奇的靈異現(xiàn)象。 例如兩個(gè)單詞 or 字符用肉眼看是完全一模一樣的,但是在計(jì)算機(jī)中讀取出來(lái)卻表示兩者不相等。進(jìn)一步的,當(dāng)我們查看這個(gè)item的編碼字符的時(shí)候,發(fā)現(xiàn)兩者確實(shí)也不一樣。那究竟是什么樣的一回事呢??

text_a = "?????" text_b = "??????"print(text_a == text_b) # False print(unicodedata.normalize("NFKD", text_a) == text_b) # True

事實(shí)上,在Unicode的編碼中,經(jīng)常會(huì)有一些特殊字符被編碼成多種 Unicode 形式。例如: 字符 U+00C7 (LATIN CAPITAL LETTER C WITH CEDILLA) 也可以被表示為下面列個(gè)字符的組合: U+0043 (LATIN CAPITAL LETTER C) 和 字符U+0327 (COMBINING CEDILLA).

這種情況下多發(fā)于那些需要包含音調(diào)的字符體系中(例如印地語(yǔ)、德語(yǔ)、西班牙語(yǔ)等),如以下字符"?"。Unicode體系中,即可以用Compose(組合)的形式U+00C7來(lái)表示這個(gè)字符。 也可以使用Decompose(分離)分別存儲(chǔ)字符(U+0043)本身和音調(diào)(U+0327)本身。

在上面的印地語(yǔ)中,出現(xiàn)問(wèn)題的主要是因?yàn)樽址?#34;?",該字符下有一個(gè)小點(diǎn),表示印地語(yǔ)中的一些音調(diào)問(wèn)題(具體參考 Nuqta)。該字符就擁有 Compose 和 Decompose 兩種Unicode表示方法, 因此才會(huì)出現(xiàn)上文中字符不等的例子。

在Python中,我們可以利用 unicodedata.normalize 函數(shù)對(duì)字符進(jìn)行標(biāo)準(zhǔn)化。標(biāo)準(zhǔn)化分為兩個(gè)方式:

  • unicodedata.normalize("NFKC", text): Normal form Composition: 將所有的文本標(biāo)準(zhǔn)化為 Compose 形式。
  • unicodedata.normalize("NFKD", text): Normal form Decomposition: 將所有的文本標(biāo)準(zhǔn)化為 Decompose 形式。

更標(biāo)準(zhǔn)的寫(xiě)法,應(yīng)該為

import unicodedata def strip_accents(s):return ''.join(c for c in unicodedata.normalize('NFD', s)if unicodedata.category(c) != 'Mn')

3.3.1 題外話:

在撰寫(xiě)本文的時(shí)候,我發(fā)現(xiàn)了一些外觀長(zhǎng)的一模一樣,并且通過(guò)normalize方法也無(wú)法歸一化的問(wèn)題。例如:

a = "?" b = "馬"print(a == b) # False print(a.encode("raw_unicode_escape")) # b'u2ee2' print(b.encode("raw_unicode_escape")) # b'u9a6c' print(unicodedata.normalize("NFKD", a) == b) # False print(unicodedata.normalize("NFKC", a) == b) # False

于是我對(duì)上述文本中的第一個(gè)『馬』進(jìn)行了一番查詢(正是文章開(kāi)頭圖片的字符),發(fā)現(xiàn):

  • 第一個(gè)馬的Category是一個(gè)Symbol,也就是說(shuō)是一個(gè)符號(hào)。
  • 第一個(gè)馬的Block屬于Radical-Block,查詢了一下,主要是在漢字中用于偏旁作用的。

那么,如果在實(shí)際應(yīng)用中,應(yīng)該如何對(duì)這兩個(gè)字符進(jìn)行歸一化呢??? 目前我也沒(méi)有 idea 。。。。。

0x04. Reference:

  • [1]. NLP哪里跑: Unicode相關(guān)的一些小知識(shí)和工具
  • [2]. Python - Unicodedata

總結(jié)

以上是生活随笔為你收集整理的rhel 8.2不识别unicode_Unicode的文本处理二三事的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。