<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          用 Python 理清編碼問題:Unicode萬國碼

          共 3718字,需瀏覽 8分鐘

           ·

          2021-04-14 21:55

          Unicode——萬國碼

          為解決語言各自為政的編碼,人們提出了Unicode編碼方案,這個(gè)方案簡單粗暴:把世界上所有語言字符統(tǒng)一編碼。Unicode的兩種方案UCS-2和UCS-4,可使用空間分別達(dá)到2^16和2^32個(gè):外星人到訪地球之前,應(yīng)該是夠用的。

          我們看幾個(gè)字符的Unicode編碼碼位(code point)是怎樣的:

          ls = 'abAB鞏★☆'
          print([ord(l) for l in ls])

          結(jié)果:[97, 98, 65, 66, 24041, 9733, 9734]。可見,字母abAB的Unicode碼位和其ASCII碼位一致,所以字符為字母時(shí)兩者兼容,而漢字鞏的碼位為24041(0x5de9),與之前的GB系列編碼47534(0xb9ae)不同,所以Unicode和GB系列編碼之間是不完全兼容的:只有ASCII部分兼容。

          所有國家的人都使用Unicode編碼之后,擴(kuò)展、亂碼問題都不復(fù)存在:所有人類語言字符都有了一個(gè)統(tǒng)一的編碼碼位,溝通中我們寫出的每個(gè)數(shù)字編碼,都有唯一的字符與他對(duì)應(yīng)。Python中chr函數(shù)返回Unicode碼位對(duì)應(yīng)的字符。

          >>> print([chr(i) for i in [123,957,24041]])
          ['{''ν''鞏']
          那么我們可以使用強(qiáng)大的Unicode進(jìn)行編碼了么?
          >>> ls = 'abAB鞏★☆'
          >>> ls.encode('Unicode')
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module> LookupError: unknown encoding: Unicode
          未知編碼Unicode!這是因?yàn)椋⒉淮嬖赨nicode碼這種編碼形式,Unicode只是一個(gè)碼位表,它只是建立了字符和整數(shù)之間的映射。至于整數(shù)碼位(code point)如何存儲(chǔ)成字節(jié),先存高位低位,有沒有特殊標(biāo)志,Unicode并不直接決定,而是交給具體編碼來考慮這些細(xì)節(jié):UTF-32,UTF-16和UTF-8。

          UTF-32 四字節(jié)為單位

          UTF-32,顧名思義,是用32位,也就是四個(gè)字節(jié)來存儲(chǔ)一個(gè)字符的編碼方案。
          >>> 'aA鞏'.encode('utf-32LE')
          b'a\x00\x00\x00A\x00\x00\x00\xe9\x5d\x00\x00'
          可見,所有的字符,都使用了四個(gè)字節(jié)來存儲(chǔ):每個(gè)字節(jié)除了Unicode碼位之外,不足用\x00來填充。此法簡單明了,Unicode碼位不用轉(zhuǎn)換,直接填充。但大量的\x00造成了極大的浪費(fèi)。
          有沒有辦法解決這種浪費(fèi)了?壓縮下用兩位行不行?

          UTF-16 兩字節(jié)為單位

          當(dāng)用UTF-16來編碼時(shí)。
          >>> 'aA鞏'.encode('utf-16LE')
          b'a\x00A\x00\xe9\x5d'
          兩個(gè)字節(jié)對(duì)絕大多數(shù)Unicode碼位來說是夠用的,不夠用的話系統(tǒng)自動(dòng)用四位表示。這是系統(tǒng)實(shí)現(xiàn),我們無需關(guān)心。UTF-16編碼后的字節(jié)序列和字符,依然能夠一一對(duì)應(yīng)起來。UTF-16其實(shí)有兩種編碼方法,分別為上例的UTF-16LE和如下的UTF-16BE,測(cè)試:
          >>> 'aA鞏'.encode('utf-16BE')
          b'\x00a\x00A\x5d\xe9'
          兩者基本一樣,只是高低字節(jié)位置發(fā)生了顛倒。LE和BE后綴,表示小字節(jié)序(little endian)和大字節(jié)序(big endian)。這是計(jì)算機(jī)內(nèi)部關(guān)于字節(jié)的MSB(大權(quán)重字節(jié))放在字節(jié)的開頭還是結(jié)尾的具體實(shí)現(xiàn)細(xì)節(jié)。

          《格列佛游記》中,小人國國民為吃雞蛋先吃大頭或小頭,針鋒相對(duì),組成了兩個(gè)軍事對(duì)立集團(tuán)big endians和little endians,相互間多次發(fā)動(dòng)戰(zhàn)爭。

          那么兩個(gè)字節(jié)就是Unicode編碼的極限了么?

          UTF-8 變長字節(jié)編碼

          能不能用可變數(shù)目的字節(jié)來存儲(chǔ)文本呢?如果存儲(chǔ)的是英文文本的話,每個(gè)字符只用一個(gè)字節(jié)就可以;漢字的話,再進(jìn)行擴(kuò)展。如此來進(jìn)一步節(jié)省存儲(chǔ)空間。答案是可以的,這就是可變長度編碼UTF-8。
          >>> 'aA鞏'.encode('utf-8')
          b'aA\xe5\xb7\xa9'
          這是目前最短的字節(jié)序列,因?yàn)閍A分別存儲(chǔ)成了一個(gè)字節(jié)。
          需要注意的是,UTF-32和UTF-16中,鞏的字節(jié)序列是0x5de9,但在UTF-8中,字節(jié)序列變成了0xe5b7a9。這說明UTF-8編碼不是簡單地把Unicode碼位直接存儲(chǔ)進(jìn)字節(jié)序列中,而是進(jìn)行了某些轉(zhuǎn)換。這些轉(zhuǎn)換,保證了英文用一位存儲(chǔ),漢語等較大字符多字節(jié)存儲(chǔ)。
          那么是如何轉(zhuǎn)換的呢?

          UTF-8 編碼轉(zhuǎn)換規(guī)則

          本部分過于細(xì)節(jié),可略過。
          UTF-8實(shí)現(xiàn)了可變長度的編碼,為解碼時(shí)區(qū)分可變長度究竟多長,需要在字節(jié)序列里使用特殊模板。UTF-8編碼遵循以下規(guī)則:
          • 0x00-0x7F之間的碼位,兼容ASCII碼,單字節(jié)直接存儲(chǔ)在以下模板 0*** ****
          • 0x80-0x7ff之間,使用兩個(gè)字節(jié)存儲(chǔ),字節(jié)模板是110* **** 10** ****
          • 0x800-0xffff之間,使用三個(gè)字節(jié)存儲(chǔ),字節(jié)模板是1110 **** 10** **** 10** ****
          • 0x10000-0x1fffff之間,使用四個(gè)字節(jié)存儲(chǔ),字節(jié)模板是1111 0*** 10** **** 10** **** 10** ****
          以漢字鞏為例,其Unicode碼位為0x6c49,二進(jìn)制位110 1100 0100 1001。位于第三行范圍,所以需要三個(gè)字節(jié)來存儲(chǔ),寫出模板,1110 **** 10** **** 10** ****,使用二進(jìn)制,從右向左填充,不足部分補(bǔ)零,可得結(jié)果1110 0110 1011 0001 1000 1001,十六進(jìn)制為0xe6 0xb7 0x89,所以鞏編碼為UTF-8的字節(jié)序列形式為0Xe6b789。
          讓我們從UTF-8編碼轉(zhuǎn)換細(xì)節(jié)中,回到UTF三種編碼的長度問題上來。

          UTF三種編碼后的長度

          以上三種編碼方式,由于壓縮率不用,導(dǎo)致文件長度也不同,以下程序比較當(dāng)文本為漢字和英語內(nèi)容時(shí),三種不同編碼的長度:
          es = 'abcdefghij'
          cs = '莫愁前路無知己,天下誰人不識(shí)君。'

          codes = ['utf-32le','utf-16le','utf-8']

          print([len(es.encode(code)) for code in codes])
          print([len(cs.encode(code)) for code in codes])
          輸出為 [40, 20, 10] [64, 32, 48] 可見,對(duì)于英文來說,UTF-8比UTF-16和UTF-32編碼都要有優(yōu)勢(shì);對(duì)漢字來說,最有優(yōu)勢(shì)的反而是UTF16編碼。這是因?yàn)閁TF-16編碼中,大部分漢字采用2Byte存儲(chǔ),而UTF-8中漢字需要三個(gè)字節(jié)存儲(chǔ)。
          在日常生活中,因?yàn)榭紤]到最大兼容性,UTF-8使用的最為廣泛。
          至此,我們從ASCII碼到GB系列編碼,再到Unicode和相應(yīng)的UTF系列編碼,一路進(jìn)化,擁有了一個(gè)包羅萬碼,不會(huì)亂碼和有較高壓縮率的字符編碼系統(tǒng)。
          可以使用了么?沒有!因?yàn)槲覀冎皇蔷幋a了文本自身,并沒有記載具體用了那個(gè)編碼:當(dāng)我們發(fā)送一份文件后,除非告訴對(duì)方,否則對(duì)方不知道應(yīng)該該用什么編碼打開它。
          解決這個(gè)問題,我們留待下篇文章分析。

          總結(jié)

          • Unicode統(tǒng)一了世界各語言字符。Unicode幾種編碼形式中;
          • UTF-32簡單,但浪費(fèi)嚴(yán)重。
          • UTF-16使用兩個(gè)字節(jié)為單位存儲(chǔ),節(jié)省了空間。
          • UTF-8使用一個(gè)字節(jié)直接存儲(chǔ),是效率、空間的平衡。


          作者:鞏慶奎,大奎,對(duì)計(jì)算機(jī)、電子信息工程感興趣。gongqingkui at 126.com

          贊 賞 作 者



          更多閱讀



          2020 年最佳流行 Python 庫 Top 10


          2020 Python中文社區(qū)熱門文章 Top 10


          5分鐘快速掌握 Python 定時(shí)任務(wù)框架

          特別推薦




          點(diǎn)擊下方閱讀原文加入社區(qū)會(huì)員

          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91狠狠综合久 | a 视频在线播放 | 天天综合网日韩7799精品 | 国产和美国黄色毛片 | 亚洲小学生妹三级毛片视频大全 |