您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何用Python理清編碼問題,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
為解決語言各自為政的編碼,人們提出了Unicode編碼方案,這個方案簡單粗暴:把世界上所有語言字符統一編碼。Unicode的兩種方案UCS-2和UCS-4,可使用空間分別達到2^16和2^32個:外星人到訪地球之前,應該是夠用的。
我們看幾個字符的Unicode編碼碼位(code point)是怎樣的:
ls = 'abAB鞏★☆' print([ord(l) for l in ls])
結果:[97, 98, 65, 66, 24041, 9733, 9734]。可見,字母abAB的Unicode碼位和其ASCII碼位一致,所以字符為字母時兩者兼容,而漢字鞏的碼位為24041(0x5de9),與之前的GB系列編碼47534(0xb9ae)不同,所以Unicode和GB系列編碼之間是不完全兼容的:只有ASCII部分兼容。
所有國家的人都使用Unicode編碼之后,擴展、亂碼問題都不復存在:所有人類語言字符都有了一個統一的編碼碼位,溝通中我們寫出的每個數字編碼,都有唯一的字符與他對應。Python中chr函數返回Unicode碼位對應的字符。
>>> print([chr(i) for i in [123,957,24041]]) ['{', 'ν', '鞏']
那么我們可以使用強大的Unicode進行編碼了么?
>>> ls = 'abAB鞏★☆' >>> ls.encode('Unicode') Traceback (most recent call last): File "<stdin>", line 1, in <module> LookupError: unknown encoding: Unicode
未知編碼Unicode!這是因為,并不存在Unicode碼這種編碼形式,Unicode只是一個碼位表,它只是建立了字符和整數之間的映射。至于整數碼位(code point)如何存儲成字節,先存高位低位,有沒有特殊標志,Unicode并不直接決定,而是交給具體編碼來考慮這些細節:UTF-32,UTF-16和UTF-8。
UTF-32,顧名思義,是用32位,也就是四個字節來存儲一個字符的編碼方案。
>>> 'aA鞏'.encode('utf-32LE') b'a\x00\x00\x00A\x00\x00\x00\xe9\x5d\x00\x00'
可見,所有的字符,都使用了四個字節來存儲:每個字節除了Unicode碼位之外,不足用\x00來填充。此法簡單明了,Unicode碼位不用轉換,直接填充。但大量的\x00造成了極大的浪費。有沒有辦法解決這種浪費了?壓縮下用兩位行不行?
當用UTF-16來編碼時。
>>> 'aA鞏'.encode('utf-16LE') b'a\x00A\x00\xe9\x5d'
兩個字節對絕大多數Unicode碼位來說是夠用的,不夠用的話系統自動用四位表示。這是系統實現,我們無需關心。UTF-16編碼后的字節序列和字符,依然能夠一一對應起來。UTF-16其實有兩種編碼方法,分別為上例的UTF-16LE和如下的UTF-16BE,測試:
>>> 'aA鞏'.encode('utf-16BE') b'\x00a\x00A\x5d\xe9'
兩者基本一樣,只是高低字節位置發生了顛倒。LE和BE后綴,表示小字節序(little endian)和大字節序(big endian)。這是計算機內部關于字節的MSB(大權重字節)放在字節的開頭還是結尾的具體實現細節。
《格列佛游記》中,小人國國民為吃雞蛋先吃大頭或小頭,針鋒相對,組成了兩個軍事對立集團big endians和little endians,相互間多次發動戰爭。
那么兩個字節就是Unicode編碼的極限了么?
能不能用可變數目的字節來存儲文本呢?如果存儲的是英文文本的話,每個字符只用一個字節就可以;漢字的話,再進行擴展。如此來進一步節省存儲空間。答案是可以的,這就是可變長度編碼UTF-8。
>>> 'aA鞏'.encode('utf-8') b'aA\xe5\xb7\xa9'
這是目前最短的字節序列,因為aA分別存儲成了一個字節。需要注意的是,UTF-32和UTF-16中,鞏的字節序列是0x5de9,但在UTF-8中,字節序列變成了0xe5b7a9。這說明UTF-8編碼不是簡單地把Unicode碼位直接存儲進字節序列中,而是進行了某些轉換。這些轉換,保證了英文用一位存儲,漢語等較大字符多字節存儲。那么是如何轉換的呢?
本部分過于細節,可略過。UTF-8實現了可變長度的編碼,為解碼時區分可變長度究竟多長,需要在字節序列里使用特殊模板。UTF-8編碼遵循以下規則:
0x00-0x7F之間的碼位,兼容ASCII碼,單字節直接存儲在以下模板 0*** ****
0x80-0x7ff之間,使用兩個字節存儲,字節模板是110* **** 10** ****
0x800-0xffff之間,使用三個字節存儲,字節模板是1110 **** 10** **** 10** ****
0x10000-0x1fffff之間,使用四個字節存儲,字節模板是1111 0*** 10** **** 10** **** 10** ****
以漢字鞏為例,其Unicode碼位為0x6c49,二進制位110 1100 0100 1001。位于第三行范圍,所以需要三個字節來存儲,寫出模板,1110 **** 10** **** 10** ****,使用二進制,從右向左填充,不足部分補零,可得結果1110 0110 1011 0001 1000 1001,十六進制為0xe6 0xb7 0x89,所以鞏編碼為UTF-8的字節序列形式為0Xe6b789。讓我們從UTF-8編碼轉換細節中,回到UTF三種編碼的長度問題上來。
以上三種編碼方式,由于壓縮率不用,導致文件長度也不同,以下程序比較當文本為漢字和英語內容時,三種不同編碼的長度:
es = 'abcdefghij' cs = '莫愁前路無知己,天下誰人不識君。' 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] 可見,對于英文來說,UTF-8比UTF-16和UTF-32編碼都要有優勢;對漢字來說,最有優勢的反而是UTF16編碼。這是因為UTF-16編碼中,大部分漢字采用2Byte存儲,而UTF-8中漢字需要三個字節存儲。在日常生活中,因為考慮到最大兼容性,UTF-8使用的最為廣泛。至此,我們從ASCII碼到GB系列編碼,再到Unicode和相應的UTF系列編碼,一路進化,擁有了一個包羅萬碼,不會亂碼和有較高壓縮率的字符編碼系統。可以使用了么?沒有!因為我們只是編碼了文本自身,并沒有記載具體用了那個編碼:當我們發送一份文件后,除非告訴對方,否則對方不知道應該該用什么編碼打開它。解決這個問題,我們留待下篇文章分析。
Unicode統一了世界各語言字符。Unicode幾種編碼形式中;
UTF-32簡單,但浪費嚴重。
UTF-16使用兩個字節為單位存儲,節省了空間。
UTF-8使用一個字節直接存儲,是效率、空間的平衡。
看完上述內容,你們對如何用Python理清編碼問題有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。