摘要:之痛原文地址譯者校正實(shí)用編程指南這是我在所做的演講。事實(shí)一和二共同造成了計(jì)算機(jī)設(shè)備結(jié)構(gòu)與世界人類(lèi)需求的一個(gè)沖突。就是為了解決之前的老的字符集問(wèn)題。值意味著,失敗時(shí)將會(huì)返回一個(gè)標(biāo)準(zhǔn)的替代字符。將使用進(jìn)行了解碼。
Unicode之痛
原文地址: http://nedbatchelder.com/text...
譯者: yudun1989 校正: sicklife
實(shí)用Unicode編程指南
這是我在 Pycon2012 所做的演講。你可以閱讀本頁(yè)的幻燈片和文字,或者直接在瀏覽器中打開(kāi) 演示 ,或者來(lái)看現(xiàn)場(chǎng)視頻。
同時(shí),點(diǎn)擊文章的圖片將會(huì)進(jìn)入所在幻燈片的的對(duì)應(yīng)位置,圖片中使用了 Symbola 字體,但是如果想要顯示一些特殊符號(hào)的話(huà),則需先將該字體下載下來(lái)。
大家好,我是Ned Batchelder.我已經(jīng)有十年的Python編程經(jīng)驗(yàn),這意味著,很多很多的時(shí)候,我與其他程序員一樣,犯過(guò)很多 Unicode 的編碼錯(cuò)誤。
如果你和其他 Python 程序員一樣,那你肯定也碰到過(guò)如下情況:你編寫(xiě)了一段很漂亮的代碼,事情看起來(lái)很順。然后某一天一個(gè)很奇怪的“方言字符”不知道從哪冒了出來(lái),你的程序中就開(kāi)始大量涌現(xiàn) UnicodeErrors 。
你好像知道這種問(wèn)題應(yīng)該怎樣解決,于是呢,就去在錯(cuò)誤出現(xiàn)的地方添加了 encode 和 decode ,但是 UnicodeError 又開(kāi)始出現(xiàn)在其他的地方。于是你又在另外一個(gè)地方添加了 decode 抑或 encode 。在你玩過(guò)一段“編碼打地鼠”游戲之后,問(wèn)題似乎被解決。
之后某一天,另一種“方言字符”又在另外一個(gè)地方出現(xiàn)了。然后你不得不又去玩這種“打地鼠”直到問(wèn)題解決掉。
現(xiàn)在你的程序終于可以運(yùn)行。但是你既煩惱又不適,這個(gè)問(wèn)題花費(fèi)了太多時(shí)間,你知道這樣解決“正確”,于是開(kāi)始憎恨自己。你對(duì) Unicode 的主要了解就是你很討厭它。
你不想去了解怪異的字符集,你只想要寫(xiě)一個(gè)你認(rèn)為不是很糟糕的程序。
你不必去玩打地鼠游戲. Unicode 會(huì)有些麻煩,但是它并不難。了解了相關(guān)知識(shí)并且加以練習(xí),你也可以方便的優(yōu)雅的解決相關(guān)問(wèn)題。
接下來(lái)我會(huì)教給你 five Facts of lie,然后給你一些專(zhuān)業(yè)建議來(lái)解決 Unicode 問(wèn)題。下面的內(nèi)容將會(huì)包含 Unicode 基本知識(shí),如何在 Python 2 和 Python 3 中來(lái)實(shí)現(xiàn)。他們有一定差異,但是你使用的基本策略都是一樣的。
世界 & Unicode
我們從 Unicode 基本知識(shí)開(kāi)始。
事實(shí)之一:計(jì)算機(jī)中的一切均為 bytes(字節(jié))。硬盤(pán)中的文件為一系列的 byte 組成,網(wǎng)絡(luò)中傳輸?shù)闹挥?byte。所有的信息,在你寫(xiě)的程序中進(jìn)進(jìn)出出的,均由 byte 組成。
孤立的 byte 是毫無(wú)意義的,所以我們來(lái)賦予它們含義。
為了表示各種文字,我們有大約 50 年的時(shí)間都在用 ASCII 碼。每一個(gè) byte 被賦予 95 種符號(hào)的一種,所以,當(dāng)我給你發(fā)送 byte 值為 65 的時(shí)候,你知道我想表達(dá)一個(gè)大寫(xiě)的 A。
ISO Latin 1,或者 8859-1 對(duì) ASCII 的 96 種字符進(jìn)行了擴(kuò)展。這也許是你用一個(gè) byte 可以做的最多的事情了。因?yàn)?byte 中沒(méi)有容量可以存儲(chǔ)更多的符號(hào)了。
在 Windows 中增加了另外 27 種字符,這種叫做 CP1252 編碼。
事實(shí)之二是,世界上的字符遠(yuǎn)遠(yuǎn)比256個(gè)要多。一個(gè)簡(jiǎn)單的byte不能夠表達(dá)世界范圍內(nèi)的字符。在你玩”編碼打地鼠”的時(shí)候,你多么的希望世界上所有的人都說(shuō)英語(yǔ),但是事實(shí)并不是這樣,人們需要更多的符號(hào)來(lái)交流。
事實(shí)一和二共同造成了計(jì)算機(jī)設(shè)備結(jié)構(gòu)與世界人類(lèi)需求的一個(gè)沖突。
當(dāng)時(shí)為了解決沖突嘗試了多種途徑。通過(guò)一個(gè) byte 來(lái)與符號(hào)或者字符進(jìn)行對(duì)應(yīng)的編碼,每一種解決途徑都沒(méi)有解決事實(shí)二中的實(shí)質(zhì)問(wèn)題。
當(dāng)時(shí)有很多一個(gè) byte 的編碼,都沒(méi)有能夠解決問(wèn)題。每一個(gè)都只能解決人類(lèi)語(yǔ)言的一部分。但是他們不能解決所有的文字問(wèn)題。
人們開(kāi)始創(chuàng)造兩個(gè) byte 的字符集,但是仍然像碎片一樣,只能夠服務(wù)于不同地域的一部分人。
當(dāng)時(shí)產(chǎn)生了不同的標(biāo)準(zhǔn),諷刺的是,他們都不足以滿(mǎn)足所有的符號(hào)的需求。
Unicode 就是為了解決之前的老的字符集問(wèn)題。Unicode 分配整形,被成為代碼點(diǎn)( UNICODE 的字符被成為代碼點(diǎn)( CODE POINTS )用 U 后面加上 XXXX 來(lái)表現(xiàn),其中, X 為16進(jìn)制的字符)來(lái)表示字符。它有 110 萬(wàn)的代碼點(diǎn),其中有十一萬(wàn)被占用,所以它可以有很多很多的空間可供未來(lái)的增長(zhǎng)使用。
Unicode 的目的是包含一切,它從 ASCII 開(kāi)始,包含了數(shù)以千計(jì)的代碼,包含這著名的—-雪人??,包含了世界上所有的書(shū)寫(xiě)系統(tǒng),而且一直在被擴(kuò)充。比如,最新的更新中,就有一大堆沒(méi)用的詞匯。
這里有六個(gè)的異國(guó) Unicode 字符。 Unicode 代碼點(diǎn)寫(xiě)成 4- , 5- ,或者 6 位的十六進(jìn)制編碼,同時(shí)有一個(gè) U 的前綴。每一個(gè)字符都有一個(gè)用 ASCII 字符規(guī)定的名稱(chēng)。
所以說(shuō) Unicode 提供了所有我們需要的字符的空間。但是我們?nèi)匀恍枰幚硎聦?shí)一中所碰到的問(wèn)題:計(jì)算機(jī)只能看懂 bytes 。我們需要一種用 bytes 來(lái)表示 Unicode 的方法這樣才可以存儲(chǔ)和傳播他們。
Unicode 標(biāo)準(zhǔn)定義了多種方法來(lái)用 bytes 來(lái)表示成代碼點(diǎn),被成為 encoding 。
UTF-8 是最流行的一種對(duì) Unicode 進(jìn)行傳播和存儲(chǔ)的編碼方式。它用不同的 bytes 來(lái)表示每一個(gè)代碼點(diǎn)。ASCII 字符每個(gè)只需要用一個(gè) byte ,與 ASCII 的編碼是一樣的。所以說(shuō) ASCII 是 UTF-8 的一個(gè)子集。
這里我們展現(xiàn)了幾個(gè)怪異字符的 UTF8 的表示方法。 ASCII 字符 H 和 I 只用一個(gè) byte 就可以表示。其他的根據(jù)代碼點(diǎn)的不同使用了兩個(gè)或者三個(gè) bytes 。盡管有些并不常用,但是一些代碼點(diǎn)使用到四個(gè) bytes。
Python 2
好,說(shuō)完了這么多理論知識(shí),我們來(lái)講一講 Python 2
在 Python2 中,有兩種字符串?dāng)?shù)據(jù)類(lèi)型。一種純舊式的文字: “str” 對(duì)象,存儲(chǔ) bytes 。如果你使用一個(gè) “u” 前綴,那么你會(huì)有一個(gè) “unicode” 對(duì)象,存儲(chǔ)的是 code points 。在一個(gè) unicode 字符串中,你可以使用反斜杠 u(u) 來(lái)插入任何的 unicode 代碼點(diǎn)。
你可以注意到 “string” 這個(gè)詞是有問(wèn)題的。不管是 “str” 還是 “unicode” 都是一種 “string” ,這會(huì)吸引叫它們都是 string ,但是為了直接還是將它們明確區(qū)分來(lái)。
如果想要在 unicode 和 bytes 間轉(zhuǎn)換的話(huà),兩者都有一個(gè)方法。 Unicode 字符串會(huì)有一個(gè) .encode 方法來(lái)產(chǎn)生 bytes , bytes 串會(huì)有一個(gè) .decode 方法來(lái)產(chǎn)生 unicode 。每個(gè)方法中都有一個(gè)參數(shù)來(lái)表明你要操作的編碼類(lèi)型。
我們可以定義一個(gè) Unicode 字符串叫做 my_unicode ,然后看這九個(gè)字符,我們使用 encode 方法來(lái)創(chuàng)建 my_unicode 的 bytes 串。會(huì)有 19 個(gè) bytes ,想你所期待的那樣。將 bytes 串來(lái) decode 將會(huì)得到 utf-8 串。
不幸的是,如果指明的編碼名稱(chēng)錯(cuò)誤的話(huà),那么 encode 和 decode 會(huì)產(chǎn)生錯(cuò)誤。現(xiàn)在嘗試 encode 我們的幾個(gè)詭異的字符到 ascii ,會(huì)失敗。因?yàn)?ascii 只能表示 0-127個(gè) 字符中的一個(gè)。然而我們的 Unicode 字符串早已經(jīng)超出了范圍。
拋出的異常為 UnicodeEncodeError ,它展現(xiàn)了你使用的編碼方式, “codec” 即(coder/decoder),展現(xiàn)了導(dǎo)致問(wèn)題的字符的位置。
解碼同樣會(huì)知道出一些問(wèn)題。現(xiàn)在我們?nèi)グ岩粋€(gè) UTF-8 字符串解碼成 ASCII ,會(huì)得到一個(gè) UnicodeDecodeError ,原因一樣, ASCII 只接受 127 內(nèi)的值,我們的 UTF-8字 符串超出了范圍。
盡管 UTF-8 不能解碼成任何的 bytes 串,我們嘗試來(lái) decode 一些垃圾信息。同樣也產(chǎn)生了 UnicodeDecodeError 錯(cuò)誤。最終, UTF-8 的優(yōu)勢(shì)是,有效的 bytes 串,將會(huì)幫助我們來(lái)創(chuàng)建高魯棒性的系統(tǒng):如果數(shù)據(jù)無(wú)效的話(huà),數(shù)據(jù)不會(huì)被接受。
當(dāng)編碼或者解碼的時(shí)候,你可以指明如果 codec 不能夠處理數(shù)據(jù)的時(shí)候,會(huì)發(fā)生什么情況。 encode 或者 decode 時(shí)候的第二個(gè)參數(shù)指明了規(guī)則。默認(rèn)的值是 “strict” ,意味著像剛才一樣,會(huì)拋出一個(gè)異常。
“replace” 值意味著,失敗時(shí)將會(huì)返回一個(gè)標(biāo)準(zhǔn)的替代字符。當(dāng)編碼的時(shí)候,替代值是一個(gè)問(wèn)號(hào),所以任何不能被編碼的值將會(huì)產(chǎn)生一個(gè) ”?”。
一些其他的 handler 非常有用。”xmlcharrefreplace” 將會(huì)產(chǎn)生一個(gè)完全替代的 HTML/XML 字符,所以 u01B4 將會(huì)變成 “?” (因?yàn)槭M(jìn)制的 01B4 是十進(jìn)制的 436 )。如果你需要將返回的值來(lái)輸出到 html 文件中的話(huà),將會(huì)非常有用。
注意要根據(jù)不同的錯(cuò)誤原因使用不同的錯(cuò)誤處理方式。”replace” 是一個(gè)處理不能被解析的數(shù)據(jù)的自衛(wèi)型方式,會(huì)丟失數(shù)據(jù)。”xmlcharrefreplace” 會(huì)保護(hù)所有的原始數(shù)據(jù),在 XML 轉(zhuǎn)義符可以使用的時(shí)候來(lái)輸出數(shù)據(jù)。
你也可以指定在解碼時(shí)的錯(cuò)誤處理方式。”ignore” 會(huì)直接將不能解碼的 bytes 丟掉。”replace” 將會(huì)直接添加 Unicode U+FFFD ,給有問(wèn)題的 bytes 來(lái)直接替換成”替換字符”。注意因?yàn)榻獯a器不能解碼這些數(shù)據(jù)。它并不知道到底有多少 Unicode 字符。解碼我們的 UTF-8 字符串成為 ASCII 制造出了 16 個(gè)”替換字符”。每個(gè) byte 不能被解析都被替換掉了。然而這些 bytes 只想要表示 6 個(gè) Unicode 字符。
Python 2 已經(jīng)試圖在處理 unicode 和 byte 串的時(shí)候變得有用些。如果你系那個(gè)要把 Unicode 字符串串和 byte 字符串來(lái)組合起來(lái)的話(huà), Python 2 將會(huì)自動(dòng)的將 byte 串來(lái)解碼成 unicode 字符串。從而產(chǎn)生一個(gè)新的 Unicode 字符串。
比如,我們想要連接 Unicode 串 “hello” 和一個(gè) byte 字符串 “world”。結(jié)果是一個(gè) Unicode 的 “hello world”。在我們看來(lái)。Python 2 將 “world” 使用 ASCII codec 進(jìn)行了解碼。這次在解碼中使用的字符集的值與 sys.getdefaultencoding() 的值相等。
這里這個(gè)系統(tǒng)中的字符集為 ASCII, 因?yàn)檫@是唯一合理的一種猜測(cè): ASCII 被如此廣泛接受,它是這么多編碼的子集,不太會(huì)是錯(cuò)誤的。
當(dāng)然,這些隱藏的編碼轉(zhuǎn)換不能免疫于解碼錯(cuò)誤。如果你想要連接 一個(gè) byte 字符串和一個(gè) unicode 字符串,并且 byte 字符串不能被解碼成 ASCII 的話(huà),將會(huì)拋出一個(gè) UnicodeDecodeError。
這就是那些可惡的 UnicodeError 的圓圈。你的代碼中包含了 unicode 和 byte 字符串,只要數(shù)據(jù)全部是 ASCII 的話(huà),所有的轉(zhuǎn)換都是正確的,一旦一個(gè)非 ASCII 字符偷偷進(jìn)入你的程序,那么默認(rèn)的解碼將會(huì)失敗,從而造成 UnicodeDecodeError 的錯(cuò)誤。
Python 2 的哲學(xué)就是 Unicode 字符串和 byte 字符串是可以混合的,它試圖去通過(guò)自動(dòng)轉(zhuǎn)換來(lái)減輕你的負(fù)擔(dān)。就像在 int 和 float 之間的轉(zhuǎn)換一樣, int 到 float 的轉(zhuǎn)換不會(huì)失敗,byte 字符串到 unicode 字符串會(huì)失敗。
Python 2 悄悄掩蓋了 byte 到 unicode 的轉(zhuǎn)換,讓程序在處理 ASCII 的時(shí)候更加簡(jiǎn)單。你付出的代價(jià)就是在處理非 ASCII 的時(shí)候?qū)?huì)失敗。
有很多方法來(lái)合并兩種字符串(一個(gè) byte 字符串和一個(gè) unicode 字符串),所有的方法都會(huì)先將 byte 轉(zhuǎn)換為 unicode,所以處理它們的時(shí)候你必須多加小心。
首先我們使用 ASCII 格式字符串,和 unicode 來(lái)結(jié)合。那么最終的輸出將會(huì)變成 unicode。返回一個(gè) unicode 字符串。
之后我們將兩個(gè)交換一下:一個(gè) unicode 格式的字符串和一個(gè) byte 串再一次合并,生成了一個(gè) unicode 字符串,因?yàn)?byte 串可以被解碼成 ASCII。
簡(jiǎn)單的去打印出一個(gè) unicode 字符串將會(huì)調(diào)用隱式的編碼:輸出總會(huì)是 bytes, 所以在 unicode 被打印之前必須被編碼成 byte 串。
接下來(lái)的事情非常不可理解:我們讓一個(gè) byte 串編碼成 UTF-8,卻得到一個(gè)錯(cuò)誤說(shuō)不能被解碼成 ASCII!這里的問(wèn)題是 byte 串不能被編碼,要記住編碼是你將 Unicode 變成了 byte 串。所以想要執(zhí)行你的操作的話(huà),Python2 需要的是一個(gè) unicode 字符串,隱式的將你的字符串解碼成 ASCII。
最后,我們將 ASCII 字符串編碼成 UTF-8。現(xiàn)在我們進(jìn)行相同的隱式編碼操作,因?yàn)樽址疄?ASCII,編碼成功。并且將它編碼成了 UTF-8 ,打印出了原始的 byte 字符串,因?yàn)?ASCII 是 UTF-8 的一個(gè)子集。
最重要的事實(shí)之三:byte 和 unicode 都非常重要,你必須將兩個(gè)都處理好。你不能假設(shè)所有的字符串都是 byte,或者所有的字符串都是 unicode,你必須適當(dāng)?shù)剡\(yùn)用它們,必要時(shí)轉(zhuǎn)換它們。
Python 3
我們看到了 Python 2 版本中有關(guān) Unicode 之痛。現(xiàn)在我們看一下 Python 3,在 Python 2 到 Python 3 中最重要的變化就是它們對(duì) Unicode 的處理。
跟 Python 2 類(lèi)似,Python 3 也有兩種類(lèi)型,一個(gè)是 Unicode,一個(gè)是 byte 碼。但是它們有不同的命名。
現(xiàn)在你從普通文本轉(zhuǎn)換成 “str” 類(lèi)型后存儲(chǔ)的是一個(gè) unicode, “bytes” 類(lèi)型存儲(chǔ)的是 byte 串。你也可以通過(guò)一個(gè) b 前綴來(lái)制造 byte 串。
所以在 Python 2 中的 “str” 現(xiàn)在叫做 “bytes”,而 Python 2 中的 “unicode” 現(xiàn)在叫做 “str”。這比起Python 2中更容易理解,因?yàn)?Unicode 是你總想要存儲(chǔ)的內(nèi)容。而 bytes 字符串只有你在想要處理 byte 的時(shí)候得到。
Python 3 中對(duì) Unicode 支持的最大變化就是沒(méi)有對(duì) byte 字符串的自動(dòng)解碼。如果你想要用一個(gè) byte 字符串和一個(gè) unicode 相連接的話(huà),你會(huì)得到一個(gè)錯(cuò)誤,不管包含的內(nèi)容是什么。
所有這些在 Python 2 中都有隱式的處理,而在 Python 3 中你將會(huì)得到一個(gè)錯(cuò)誤。
另外如果一個(gè) Unicode 字符串和 byte 字符串中包含的是相同的 ASCII 碼,Python 2 中將認(rèn)為兩個(gè)是相等的,而在 Python 3 中不會(huì)。這樣做的結(jié)果是 Unicode 中的鍵不能找到 byte 字符串中的值,反之亦然,然而在 Python 2 中是可行的。
這樣徹底了改變了 Python 3 中的 Unicode 痛楚之源。在 Python 2 中,只要你使用 ASCII 數(shù)據(jù),那么混合 Unicode 和 byte 將會(huì)成功,而在 Python 3 會(huì)直接忽略數(shù)據(jù)而失敗。
這樣的話(huà),在 Python 2 中所遇到的,你認(rèn)為你的程序是正確的但是最后發(fā)現(xiàn)由于一些特殊字符而失敗的錯(cuò)誤就會(huì)避免。
Python 3 中,你的程序馬上就會(huì)產(chǎn)生錯(cuò)誤,所以即使你處理的是 ASCII 碼,那你也必須處理 bytes 和 Unicode 之間的關(guān)系。
Python 3 中對(duì)于 bytes 和 unicode 的處理非常嚴(yán)格,你被迫去處理這些事情。這曾經(jīng)引起爭(zhēng)議。
這樣處理的原因之一是對(duì)讀取文件的變化,Python 對(duì)于讀取文件有兩種方式,一種是二進(jìn)制,一種是文本。在 Python 2 中,它只會(huì)影響到行尾符號(hào),甚至在 Unix 系統(tǒng)上的時(shí)候,基本沒(méi)有區(qū)別。
在 Python 3中。這兩種模式將會(huì)返回不同的結(jié)果。當(dāng)你用文本模式打開(kāi)一個(gè)文件時(shí)不管你是用的 “r” 模式或者默認(rèn)的模式,讀取成功的文件將會(huì)自動(dòng)轉(zhuǎn)碼成 unicode ,你會(huì)得到 str 對(duì)象。
如果你用二進(jìn)制模式打開(kāi)一個(gè)文件,在參數(shù)中輸入 “rb” ,那么從文件中讀取的數(shù)據(jù)會(huì)是 bytes,對(duì)它們沒(méi)有任何處理。
隱式的對(duì) bytes 到 unicode 的處理使用的是 locale.getpreferedencoding() ,然而它有可能輸出你不想要的結(jié)果。比如,當(dāng)你讀取 hi_utf8.txt 時(shí),他被解碼成語(yǔ)言偏好中所設(shè)置的編碼方式,如果我們這些例子在 Windows 中創(chuàng)建的話(huà),那么就是 “cp1252” 。像 ISO 8859-1, CP-1252 這些可以得到任意的 byte 值,所以不會(huì)拋出 UnicodeDecodeError ,當(dāng)然也意味著它們會(huì)直接將數(shù)據(jù)解碼成 CP-1252,制造出我們并不需要的垃圾信息。
為了文件讀取正確的話(huà),你應(yīng)該指明想要的編碼。open 函數(shù)現(xiàn)在已經(jīng)可以通過(guò)參數(shù)來(lái)指明編碼。
減輕痛苦
好,那么如何來(lái)減少這些痛苦?好消息是減輕痛苦的規(guī)則非常簡(jiǎn)單,在Python 2和 Python 3中都比較適用。
正如我們?cè)谑聦?shí)一中所看到的,在你的程序中進(jìn)進(jìn)出出的只有 bytes, 但是在你的程序中你不必處理所有的 bytes。最好的策略是將輸入的 bytes 馬上解碼成 unicode。你在程序中均使用 unicode ,當(dāng)在進(jìn)行輸出的時(shí)候,盡早將之編碼成 bytes 。
制造一個(gè) Unicode 三明治, bytes 在外, Unicode 在內(nèi)。
要記著,有時(shí)候一些庫(kù)將會(huì)幫助你完成類(lèi)似的事情。一些庫(kù)可能讓你輸入 unicode,輸出 unicode,它會(huì)幫你完成轉(zhuǎn)換的功能。比如 Django 在它的 json 模塊中提供 Unicode。
第二條規(guī)則是:你需要知道你現(xiàn)在處理的是哪種類(lèi)型的數(shù)據(jù),在你的程序中任何一個(gè)位置,你需要知道你處理的是 byte 串還是一個(gè) unicode 串。它不能是一種猜測(cè),而應(yīng)該被設(shè)計(jì)好。
另外,如果你有一個(gè) byte 串的話(huà),如果你想對(duì)它進(jìn)行處理。那么你應(yīng)該知道它是怎樣的編碼。
在對(duì)你的代碼進(jìn)行 debug 的時(shí)候,不能僅僅將之打印出來(lái)來(lái)看它的類(lèi)型。你應(yīng)該查看它的 type ,或者查看它 repr 之后的值來(lái)查看你的數(shù)據(jù)到底是什么類(lèi)型。
我曾經(jīng)說(shuō)過(guò),你應(yīng)該了解你的 byte 字符串的編碼類(lèi)型。好這里要我講事實(shí)四:你不能通過(guò)檢查它來(lái)判斷這個(gè)字符串編碼的類(lèi)型。你應(yīng)該通過(guò)其他途徑來(lái)了解。比如很多協(xié)議中將會(huì)指明編碼類(lèi)型。這里我們給出 HTTP, HTML, XML, Python 源文件中的例子。你也可以通過(guò)預(yù)先的指定來(lái)了解編碼。比如數(shù)據(jù)源碼中可能會(huì)指明編碼。
有一些方式可以來(lái)猜測(cè)一些 bytes 的編碼類(lèi)型。但是僅僅是猜測(cè)。能夠確定的唯一方式是通過(guò)其他方式。
這里是給出一些怪異的字符的編碼猜測(cè)。我們用UTF-8 便民店的一些字符,被不同的解碼方式解碼之后的輸出。你可以看見(jiàn)。有時(shí)候用不正確的解碼方式解碼可能會(huì)輸出正確,但是會(huì)輸出錯(cuò)誤的字符。你的程序不能告訴你這些解析錯(cuò)誤了。只有當(dāng)用戶(hù)察覺(jué)到的時(shí)候你才會(huì)發(fā)現(xiàn)錯(cuò)誤。
這是事實(shí)四的一個(gè)好例子:同樣的 bytes 流通過(guò)不同的解碼器是可以解碼的。而 bytes 本身不能指明它自己用的哪種編碼方式。
順便說(shuō)一下,這些垃圾信息的顯示只遵循一個(gè)規(guī)則,那就是亂碼。
不幸的是,bytes 流會(huì)根據(jù)自己的來(lái)源不同而進(jìn)行不同的編碼,有時(shí)候我們指明的編碼方式可能是錯(cuò)誤的。比如你有可能將一個(gè) HTML 從網(wǎng)上抓取下來(lái),HTTP 頭中指明編碼方式是 8859-1, 然而實(shí)際上的編碼確是 UTF-8。
在一些情況下編碼方式的不匹配可能會(huì)產(chǎn)生亂碼,而有些時(shí)候,則會(huì)產(chǎn)生 UnicodeError。
不用說(shuō)。你應(yīng)該測(cè)試你的 Unicode 支持。為了這樣。你首先應(yīng)該在你的代碼中首先去先把 Unicode 來(lái)提取出。如果你只會(huì)說(shuō)英語(yǔ),這可能會(huì)有些困難。因?yàn)橛行?Unicode 數(shù)據(jù)會(huì)比較難以讀。幸運(yùn)的是,大部分時(shí)候一些復(fù)雜結(jié)構(gòu)的 Unicode 字符串還是比較具有可讀性的。
這里是一個(gè)例子。ASCII 文本中可以讀的文本,和倒置的文本。這些文本的一些有時(shí)候是一些青年人會(huì)粘貼到社交網(wǎng)絡(luò)中。
根據(jù)你的程序,你有可能在 Unicode 的道路中越挖越深。還有很多很多的細(xì)節(jié)我這里沒(méi)有解釋清楚。可以被涉及到。我們稱(chēng)之為事實(shí)五。因?yàn)槟悴槐厝?duì)此了解太詳細(xì)。
復(fù)習(xí)一下,我們有五個(gè)不可忽視的事實(shí):
程序中所有的輸入和輸出均為 byte
世界上的文本需要比 256 更多的符號(hào)來(lái)表現(xiàn)
你的程序必須處理 byte 和 unicode
byte 流中不會(huì)包含編碼信息
指明的編碼有可能是錯(cuò)誤的
這是你在編程中保持 Unicode 清潔的三個(gè)建議:
Unicode 三明治:盡可能的讓你程序處理的文本都為 Unicode 。
了解你的字符串。你應(yīng)該知道你的程序中,哪些是 unicode, 哪些是 byte, 對(duì)于這些 byte 串,你應(yīng)該知道,他們的編碼是什么。
測(cè)試 Unicode 支持。使用一些奇怪的符號(hào)來(lái)測(cè)試你是否已經(jīng)做到了以上幾點(diǎn)。
如果你遵循以上建議的話(huà),你將會(huì)寫(xiě)出對(duì) Unicode 支持很好的代碼。不管 Unicode 中有多么不規(guī)整的編碼你的程序也不會(huì)掛掉。
一些其他你可能需要的資源
Joel Spolsky 編寫(xiě)的 The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!) 概括了 Unicode 的工作方式和原因。雖然沒(méi)有 Python 的內(nèi)容,但是比我解釋的詳細(xì)多了!
如果你需要處理一些語(yǔ)義上的 Unicode 字符問(wèn)題。那么 unicodedata module 也許會(huì)對(duì)你有些幫助。
如果你希望找一些 Unicode 來(lái)測(cè)試的話(huà),網(wǎng)上各種的 編碼文本計(jì)算器 會(huì)對(duì)你很有幫助。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/42330.html
摘要:搭建多頁(yè)面應(yīng)用在往下看之前請(qǐng)確保先上個(gè)涼的吃著目錄結(jié)構(gòu)編譯結(jié)果配置文件腳本存放處項(xiàng)目開(kāi)發(fā)中一些常用的方法主要存放和請(qǐng)求有關(guān)的靜態(tài)文件模版文件項(xiàng)目開(kāi)發(fā)中一些常用的方法其實(shí)我覺(jué)得可以和放在一塊,但是個(gè)人習(xí)慣還是分開(kāi)啦開(kāi)始擼代碼如何創(chuàng)建多頁(yè)面應(yīng)用 webpack4 搭建 react 多頁(yè)面應(yīng)用 在往下看之前請(qǐng)確保nodejs > 8.X 先上個(gè)涼的吃著 目錄結(jié)構(gòu) . ├── dist ...
摘要:摘要通過(guò)阿里云容器服務(wù)開(kāi)啟你的數(shù)據(jù)服務(wù)之旅一云上運(yùn)維自建數(shù)據(jù)庫(kù)之痛,使用容器服務(wù)自動(dòng)恢復(fù)數(shù)據(jù)庫(kù)實(shí)例概述本文為大家介紹一種容器化的數(shù)據(jù)服務(wù),通過(guò)使用云盤(pán)自動(dòng)掛載實(shí)現(xiàn)的塊存儲(chǔ)來(lái)做到數(shù)據(jù)庫(kù)的免運(yùn)維恢復(fù)。 摘要: 通過(guò)阿里云Kubernetes容器服務(wù),開(kāi)啟你的數(shù)據(jù)服務(wù)之旅 (一)云上運(yùn)維自建數(shù)據(jù)庫(kù)之痛,使用容器服務(wù)自動(dòng)恢復(fù)數(shù)據(jù)庫(kù)postgresql實(shí)例 概述 本文為大家介紹一種容器化的數(shù)據(jù)服...
摘要:所以,解決方案是強(qiáng)制要求從整個(gè)項(xiàng)目的頂層用來(lái)設(shè)置端正的路徑。這個(gè)做法是官方推薦的,也是合邏輯的,即一個(gè)完整的項(xiàng)目運(yùn)行就應(yīng)當(dāng)以項(xiàng)目為入口來(lái)運(yùn)行所有的子或子。經(jīng)過(guò)不斷的實(shí)踐,發(fā)現(xiàn)他們大都沒(méi)說(shuō)清楚上下文,甚至沒(méi)有告訴完整的解決方案。 參考Python官方:Packages?參考:Python相對(duì)導(dǎo)入一處不解參考:使用相對(duì)路徑名導(dǎo)入包中子模塊 理解Package Python里,就像所有的.p...
閱讀 2814·2021-11-16 11:44
閱讀 978·2021-10-09 09:58
閱讀 4503·2021-09-24 09:48
閱讀 4358·2021-09-23 11:56
閱讀 2415·2021-09-22 15:48
閱讀 1902·2021-09-07 10:07
閱讀 3213·2021-08-31 09:46
閱讀 514·2019-08-30 15:56