<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    海上月明

    editer by sun
    posts - 162, comments - 51, trackbacks - 0, articles - 8
       :: 首頁(yè) :: 新隨筆 ::  :: 聚合  :: 管理

    [轉(zhuǎn)] 復(fù)雜的Unicode,疑惑的Python

    Posted on 2009-07-20 18:44 pts 閱讀(2646) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): Python

    猛然間看到這篇文章,才發(fā)現(xiàn)原來(lái)自己理解的uniocde還是表面,這篇文章又說(shuō)明了很多深層次的內(nèi)容,值得一看。

    From:http://jawahh.sjtubbs.org/2008/07/unicodepython.html

    2008年7月21日

    復(fù)雜的Unicode,疑惑的Python

    Python 3000決定采用Unicode作為字符的默認(rèn)編碼。這不是什么新聞了,也是國(guó)際化的大勢(shì)所趨。但實(shí)際上似乎沒(méi)有那么簡(jiǎn)單。最近python-dev郵件列表吵的一個(gè)問(wèn)題就很有意思。

    7月2日,一個(gè)叫Jeroen Ruigrok van der Werven的人以UCS2/UCS4 default 為 標(biāo)題說(shuō)了問(wèn)題。Python雖然采用unicode作為默認(rèn)字符,但語(yǔ)言?xún)?nèi)部用什么方法表示unicode字符串并沒(méi)有一致的規(guī)定。在編譯的時(shí)候可以選擇 用UCS2或者UCS4編碼。作者說(shuō),隨著unicode中的CJK象形字?jǐn)U展B(CJK ideographs extension B)已經(jīng)加入最新的unicode規(guī)范,擴(kuò)展C已經(jīng)在投票表決階段,擴(kuò)展D已經(jīng)在開(kāi)發(fā),使用UCS2編碼已經(jīng)不能滿(mǎn)足預(yù)期未來(lái)的應(yīng)用了。所以應(yīng)該默認(rèn)使用 UCS4編碼。而且作者認(rèn)為允許UCS2和UCS4兩種編碼會(huì)產(chǎn)生編程一致性的問(wèn)題。比如,在用UCS2編碼的python 3中:

    >>> len("\N{MUSICAL SYMBOL G CLEF}")
    2
    而在用USC4編碼中的python 3中:
    >>> len("\N{MUSICAL SYMBOL G CLEF}")
    1

    然后這個(gè)問(wèn)題就引起了一場(chǎng)大混戰(zhàn)。這個(gè)問(wèn)題到底是什么問(wèn)題呢?到底為什么會(huì)允許那么奇怪的事情發(fā)生:同一個(gè)字符串在不同的編譯版本中長(zhǎng)度不一樣呢?其實(shí)這個(gè)問(wèn)題根植在unicode復(fù)雜的規(guī)范和歷史中。

    Unicode的一個(gè)中文名字叫“萬(wàn)國(guó)碼”。這個(gè)翻譯很明確指出了Unicode的任務(wù),為人類(lèi)使用的文字都編一個(gè)號(hào)碼,解決他們?cè)谟?jì)算機(jī)中共存的 問(wèn)題,消除計(jì)算機(jī)世界里面萬(wàn)碼奔騰的兼容性問(wèn)題。現(xiàn)在unicode 5.1規(guī)范已經(jīng)于2008-04-04發(fā)布,而且也確實(shí)很大部分消除了計(jì)算機(jī)世界里面兼容問(wèn)題,成為了事實(shí)規(guī)范。現(xiàn)在哪個(gè)稍大的新程序不支持 Unicode,是說(shuō)不過(guò)去的。不過(guò)這不表示unicode里面沒(méi)有什么犄角旮旯影響著大家的使用。

    第一個(gè)問(wèn)題是,unicode到底要給什么編號(hào)?對(duì)中文來(lái)說(shuō),當(dāng)然是給字編號(hào)——在中文里面是這樣,但unicode并不是中文編碼,這個(gè)世界的書(shū) 寫(xiě)文字五花八門(mén),中文概念里面的字在其他書(shū)寫(xiě)文字里面并不一定存在。在這里要澄清的兩個(gè)概念是glyph和character。Glyph是文字的圖像, 是我們書(shū)寫(xiě)的最小單元,是屏幕上看到的,打印機(jī)上打出來(lái)的最小單元。Character是一種邏輯上和語(yǔ)言學(xué)上的描述,它并不完全等同于Glyph。 Glyph和Character之間有多種組合發(fā)生。一種情況是一個(gè)Glyph代表多個(gè)character。舉中文里面的多音字說(shuō)明,比如“沒(méi)”字,它有 兩個(gè)讀音,而且意義完全不一樣,但只有一個(gè)表現(xiàn)形式。“沒(méi)”代表著一個(gè)glyph,代表著兩個(gè)character。如果再加上日語(yǔ)和韓語(yǔ),這種同一個(gè) glyph卻有多個(gè)character的情況就更多了。還有一個(gè)情況是多個(gè)character才能組成一個(gè)glyph。主要是一些字母的音調(diào)問(wèn)題。中文里 面也有這個(gè)問(wèn)題,比如“e”和“ˊ”這兩個(gè)character可以組成一個(gè)glyph“é”。還有一種我們不太熟悉的情況是多個(gè)glyph只是一個(gè) character,主要出現(xiàn)在阿拉伯文中。在阿拉伯文中,一個(gè)字母在不同的書(shū)寫(xiě)情況下可能有不同的表現(xiàn)形式(glyph)。還有一種情況 是,character沒(méi)有對(duì)應(yīng)的glyph,比如我們常見(jiàn)的回車(chē)符。事實(shí)上,unicode并沒(méi)有給出一個(gè)標(biāo)準(zhǔn)的說(shuō)法說(shuō)明到底給什么編號(hào),它走的是務(wù)實(shí) 主義。Unicode大致可以說(shuō)的是給character編號(hào),但也會(huì)照顧到各種語(yǔ)言的現(xiàn)實(shí),會(huì)給glyph編號(hào)。對(duì)于編程,一個(gè)簡(jiǎn)單的計(jì)算字符串長(zhǎng)度就 會(huì)發(fā)生歧義。到底我們是計(jì)算unicode字符串的character數(shù)量還是glyph數(shù)量?在ascii編碼中沒(méi)有這個(gè)問(wèn)題,因?yàn)樗? character和glyph是統(tǒng)一的。Unicode解決這個(gè)問(wèn)題的方法是不僅僅給character編號(hào),還給每個(gè)character編訂了 unicode character property。軟件可以計(jì)算character數(shù)量,也可以根據(jù)character查詢(xún)屬性,用于計(jì)算和顯示glyph。

    第二個(gè)問(wèn)題是,unicode到底打算使用多少個(gè)編號(hào)?現(xiàn)在的Unicode使用了21bit的數(shù)字去編號(hào)。目前看來(lái)21bit在可預(yù)見(jiàn)的將來(lái)是足 夠使用的——除非人類(lèi)發(fā)現(xiàn)了外星人文明,需要為他們編號(hào)。現(xiàn)在的unicode編號(hào)從0x0-0x10FFFF分為17個(gè)Plane,編號(hào)從0-16。從 0x0-0xFFFD為BMP(Basic Multilingual Plane),也就是前16bit,集中了大多現(xiàn)代書(shū)寫(xiě)系統(tǒng);從0x10000-0x1FFFD為SMP(Supplementary Mulitilingual Plane),包括了大多在歷史上曾經(jīng)使用的書(shū)寫(xiě)系統(tǒng);從0x20000-0x2FFFD為SIP(Supplementary Ideographic Plane),用于每年新增加的象形文字;然后11個(gè)plane尚未使用;Plane 14(0xE0000-0xEFFFD)為SSP(Supplementary Special-Purpose Plane),存放一些爭(zhēng)議性比較大的字符(語(yǔ)義上比較模糊或者會(huì)給文字處理帶來(lái)麻煩),使用這些字符都需要多加小心;后面兩個(gè)Plane 15(0xF0000-0xFFFFD)和Plane 16(0x100000-0x10FFFD)為保留區(qū),任何人都可以私自定義這個(gè)區(qū)域,當(dāng)然Unicode規(guī)范也不保證這些區(qū)域可以在異構(gòu)系統(tǒng)上順利交 換。還有一個(gè)特殊字符編號(hào)0xFEFF是BOM(Byte Order Mark),包括它對(duì)判斷Byte Order有特殊用途,所以它的另外一面0xFFFE也就被規(guī)定為非Unicode字符(也就是為什么Plane的結(jié)束都是0xFFFD的原因)。上面的 說(shuō)法看起來(lái)沒(méi)有什么太大的問(wèn)題,但這不是故事的全部。最開(kāi)始的Unicode只打算用16bit的數(shù)字,也就是現(xiàn)在的BMP去實(shí)現(xiàn)它的目的,這個(gè)跟當(dāng)年的 兼容和效率考量有關(guān)。但這顯然是不夠的,尤其對(duì)于龐大的CJK象形文字——至今Unicode已經(jīng)包含了7萬(wàn)多個(gè)CJK象形文字。這是個(gè)不幸的歷史。所以 早期的Unicode實(shí)現(xiàn)中,并沒(méi)有考慮到16bit以外的問(wèn)題。比如大量使用的Windows和Java構(gòu)建的系統(tǒng)。Unix系統(tǒng)倒是塞翁失馬焉知非 福,對(duì)Unicode的支持比較晚,所以大多都是用32bit去表示21bit的unicode編號(hào)。歷史的悲劇就這么產(chǎn)生了,雖然都是Unicode, 但歷史遺留系統(tǒng)和現(xiàn)代系統(tǒng)的不同表示還是給所有希望實(shí)現(xiàn)Portability的應(yīng)用帶來(lái)尷尬的處境。Python的UCS2/UCS4問(wèn)題就是其中之 一,但這不是造成這個(gè)問(wèn)題的全部原因,還有下一個(gè)原因。

    第三個(gè)問(wèn)題是,unicode到底是什么?這是個(gè)很?chē)?yán)肅的問(wèn)題。Unicode只是字符編號(hào),字符編號(hào)的屬性,以及相關(guān)說(shuō)明的集合。Unicode 不是平常所說(shuō)的編碼(Encoding)。Unicode規(guī)范只是規(guī)定了每個(gè)字符的編號(hào)(Code Point)。雖然它是為計(jì)算機(jī)設(shè)計(jì)的規(guī)范,但這個(gè)編號(hào)和計(jì)算機(jī)如何存儲(chǔ),如何表示這些字符沒(méi)有直接關(guān)系。在這一層,叫做CCS(Coded Character Set)。理論上如何表示Unicode字符是應(yīng)用程序的自由,喜歡怎么表示就怎么表示,只要你的表示方法能找到字符對(duì)應(yīng)的Code Point就行。當(dāng)然,大家不能讓這種混亂出現(xiàn),所以有了CEF(Character Encoding Form)這一層。這一層關(guān)注的是在計(jì)算機(jī)理論上如何從數(shù)字到映射Code Point,也就是如何在8bit為單位的計(jì)算機(jī)系統(tǒng)中表示21bit的Unicode Code Point。其中UCS2,UCS4,UTF32,UTF16,UTF8等等都是實(shí)際上使用的方案。理論上映射和實(shí)踐上映射不完全一樣,實(shí)踐上還要考慮異 構(gòu)系統(tǒng)的可交換特性,也就是解決大小端問(wèn)題的CES(Character Encoding Scheme)。所以又會(huì)有UTF32-LE,UTF32-BE,UTF16-BE,UTF16-LE,UTF8(對(duì)的,UTF8是CEF,也是 CES)。還有最后一層,是TES(Transfer Encoding Syntax)。這一層解決的問(wèn)題是在特定傳輸環(huán)境中的編碼問(wèn)題,比如把UTF8字符串再用base64編碼用于電子郵件傳輸。跟Python有關(guān)的是 CEF這一層。前面說(shuō)過(guò),歷史上Unicode的code point是16bit的,所以無(wú)論是UCS2,UCS4,UTF32,UTF16,UTF8都可以相安無(wú)事。對(duì)于前四者來(lái)說(shuō),都是一個(gè)code unit對(duì)應(yīng)一個(gè)code point(code unit是CEF的最小單位,對(duì)于UCS4和UTF32是32bit,對(duì)于UCS2和UTF16是16bit,對(duì)于UTF8是8bit);對(duì)于UTF8來(lái) 說(shuō)是1到3個(gè)code unit對(duì)應(yīng)一個(gè)code point。這時(shí)候的UCS4和UTF32是等價(jià)的,UCS2和UTF16是等價(jià)的。后來(lái)unicode擴(kuò)展為21位了。對(duì)于UCS4和UTF32來(lái)說(shuō), 還是一個(gè)code unit對(duì)應(yīng)一個(gè)code point,對(duì)應(yīng)用程序來(lái)說(shuō)變化不大。而對(duì)于UTF8來(lái)說(shuō),變成了1-4個(gè)code unit對(duì)應(yīng)一個(gè)code point(為什么是擴(kuò)展到21bit這么奇怪的數(shù)字呢?我猜測(cè)是為了UTF8的效率,因?yàn)閁TF8中4個(gè)code unit正好最多可以表示21bit的code point),因?yàn)閁TF8本來(lái)就是長(zhǎng)度可變的編碼,問(wèn)題也不大。但對(duì)UCS2和UTF16來(lái)說(shuō),問(wèn)題就比較頭疼了。UCS2和UTF16本來(lái)是固定長(zhǎng)度 編碼,但現(xiàn)在無(wú)論如何也不可能用16bit的存儲(chǔ)表示21bit的code point。UCS2和UTF16在這里就分道揚(yáng)鑣了。UCS2的處理方法很暴力,只保留低16位信息,忽略高5位的信息,也就是只兼容BMP中的 code point。而UTF16就變成了可變長(zhǎng)度編碼。解決方法是在BMP中劃分出兩個(gè)保留區(qū)域,分別是0xD800-0xDBFF的High Surrogate Area和0xDC00-0xDFFF的Low Surrogate Area。編碼方案是,假如有一個(gè)大于0xFFFF的code point是X,那么讓Y=X-0x10000;Y顯然是介于0x00和0xFFFFF之間的20bit數(shù)據(jù)(這也就是為什么unicode雖然擴(kuò)展到 21bit,但只有17個(gè)plane——理論上21bit可以表示32個(gè)plane)。假如Y這個(gè)數(shù)字的分隔為高10bit和低10bit(假如是 xxxxxxxxxxyyyyyyyyyy),那么X的UTF16編碼110110xxxxxxxxxx 110111yyyyyyyyyy,正好落在Surrogate Area里面。就這樣UTF16變成了長(zhǎng)度可變編碼。用Python表示就是:

    high = ((X-0x10000)>>10)&0x3FF+0xD800
    low = (X-0x10000)&0x3FF+0xDC00

    但對(duì)使用UTF16編碼的程序來(lái)說(shuō),一個(gè)字符串的code point數(shù)量(也就是unicode character的數(shù)量)和code unit的數(shù)量不再是恒等的——如果代碼里面曾經(jīng)簡(jiǎn)單的恒等這兩個(gè)數(shù)量,代碼就出錯(cuò)了。

    回到Python的問(wèn)題上,由于歷史原因,現(xiàn)有的系統(tǒng)即有使用UCS2(Windows,Java——Java從1.5開(kāi)始改為支持UTF16)也 有使用UCS4(Unix/Linux)的。為了在各個(gè)系統(tǒng)上的最大兼容性,Python的Unicode字符串在內(nèi)存中的表示方式一直都有兩種,在編譯 的時(shí)候指定(有--with-wide-unicode的時(shí)候用UCS4)。對(duì)于同一個(gè)不在BMP范圍內(nèi)的字符"\N{MUSICAL SYMBOL G CLEF}",UCS4的Python內(nèi)部表示成一個(gè)UCS4 code unit,計(jì)算長(zhǎng)度的時(shí)候自然就是等于一,因?yàn)閏ode unit的數(shù)量和code point的數(shù)量是恒等的;但UCS2的Python為了不丟失信息,首先用UTF16的編碼方式把不在BMP范圍內(nèi)的字符編碼成兩個(gè)UTF16 code unit,但計(jì)算長(zhǎng)度的時(shí)候,返回的是code unit的數(shù)量,而不是code point的數(shù)量!所以郵件列表上有人說(shuō),UCS2的Python用UTF16的編碼處理了字符輸入,又按照UCS2的方式在內(nèi)部使用。UCS2和 UTF16之間的混淆不清大概就是這個(gè)問(wèn)題的根源。

    平心而論,UTF16并不是一個(gè)糟糕的編碼。它的優(yōu)點(diǎn)是對(duì)于大多常用的字符(在BMP范圍內(nèi)的)更緊湊,無(wú)論是分割字符,計(jì)算字符,都和UTF32 一樣。但問(wèn)題就在于不是BMP范圍內(nèi)的字符。要以code point為單位處理這些字符的UTF16編碼需要跟復(fù)雜和低效的算法,比如隨機(jī)訪問(wèn)字符串中的某個(gè)字符從O(1)變成了O(N)。Java也是有這個(gè)問(wèn) 題的語(yǔ)言,它曾經(jīng)是UCS2,但從1.5開(kāi)始,增加了一套處理UTF16字符的API(String.codePointCount / String.codePointAt / String.codePointBefore / String.offsetByCodepoints)。所以在Java中,原來(lái)的代碼會(huì)保持原來(lái)的UCS2處理方式,當(dāng)你要使用超過(guò)BMP范圍的字符 時(shí),可以使用新的API處理;這樣在兼容和正確處理之間找一個(gè)妥協(xié)方案。但Python由于用了兩種內(nèi)部表示方案,問(wèn)題就變得更復(fù)雜。程序員不僅要注意到 code unit和code point的區(qū)別,還要注意到UCS4和UCS2中的code unit區(qū)別。

    郵件列表中爭(zhēng)吵到最后的結(jié)果大概是在文檔中增加對(duì)這些區(qū)別的說(shuō)明,同時(shí)增加一套新的API用于按照code point為單位處理(假如有人做的話(huà)),并不改變舊有的API的行為(isalpha之類(lèi)的API可以改變,因?yàn)椴挥绊懠嫒菪裕8F(xiàn)實(shí)世界 javascript:void(0)一樣,歷史問(wèn)題總是不能完美解決的。

    最后推薦一本書(shū),O'REILLY出版的Fonts & Encoding,前半部分關(guān)于unicode的討論可以學(xué)到不少關(guān)于unicode的知識(shí)。


    評(píng)論

    # re: [轉(zhuǎn)] 復(fù)雜的Unicode,疑惑的Python  回復(fù)  更多評(píng)論   

    2009-10-26 21:21 by 埃保常
    unicode太復(fù)雜了。以前自己以為搞懂過(guò),后來(lái)就忘了。沒(méi)治
    主站蜘蛛池模板: 亚洲天堂男人天堂| a在线视频免费观看| 亚洲黄色网站视频| 精品亚洲成α人无码成α在线观看 | 亚洲女人18毛片水真多| 亚洲视频人成在线播放| 大学生a级毛片免费观看| 最近免费中文字幕大全免费| 一本岛v免费不卡一二三区| 国产亚洲sss在线播放| 91亚洲国产成人精品下载| 亚洲乱码一区二区三区在线观看| 全部免费毛片在线| 免费无码又爽又高潮视频| 日本亚洲免费无线码| 7x7x7x免费在线观看| 热久久这里是精品6免费观看| 成人亚洲国产精品久久| 亚洲欧洲国产综合AV无码久久| 亚洲综合无码一区二区三区| 亚洲国产一区二区三区青草影视| 亚洲欧洲无码AV电影在线观看 | 色窝窝亚洲av网| 亚洲性无码一区二区三区| 亚洲国产情侣一区二区三区| 亚洲视频一区在线| 久久精品亚洲一区二区三区浴池 | xxxxxx日本处大片免费看| a高清免费毛片久久| 又粗又长又爽又长黄免费视频| 精品在线视频免费| 特级毛片A级毛片100免费播放| 妇女自拍偷自拍亚洲精品| 极品色天使在线婷婷天堂亚洲| 亚洲AV永久无码精品网站在线观看| 亚洲一区电影在线观看| 亚洲人成综合网站7777香蕉| 亚洲AV无码一区二区三区牛牛| 亚洲情A成黄在线观看动漫软件| 精品国产日韩久久亚洲| 亚洲成a∨人片在无码2023|