正確處理文本,特別是正確處理Unicode。是個老生常談的問題,有時甚至會難倒經驗豐富的開發者。並非由於這個問題很難,而是由於對軟件中的文本,開發者沒有正確理解一些關鍵概念及其表示方法。在StackOverflow上搜索關於UnicodeDecodeError相 關的問題,能夠看到不少人都有這樣的誤解。這些錯誤的概念能夠追溯到Unicode出現以前。那時許多現今的開發者還沒入職,也包括我本身。若是這些錯誤 的概念沒有散佈開來,其實不是個問題。如今不少人都有這些錯誤概念,部分緣由是由於有些很是流行的語言傳播,甚至固化了這些錯誤概念,使得糾正起來反而變 得很困難。html
根據對Unicode的支持狀況,編程語言能夠劃分爲4類:python
那麼,Unicode究竟是什麼,咱們在Unicode上犯了哪些錯誤?Joel這篇The absolute minimum every software developer absolutely, positively must know about unicode絕對是每一個軟件開發者必須閱讀的文章。爲了爲簡潔起見,以及照顧那些天生耐心不夠的朋友,我會在本文中對其進行總結。數據庫
基本事實是,若想正確的處理文本,就必須瞭解字符的抽象概念。不嚴謹的定義一下,字符表示的是文本中的單個符號。更重要的是,一個字符不是一個字 節。我再強調一遍!一個字符不是一個字節!!!並且,一個字符有許多表示方法,不一樣的表示方法會使用不一樣的字節數。就像前面我說的那樣,字符就是文本中最 小的單元。編程
Unicode以你們都承認的方式定義了一系列的字符。能夠將Unicode理解成一個字符數據庫,每一個字符都與惟一的數字關聯,稱爲code point。這樣,英文大寫字母A的codepoint是U+0041
。而歐元符號的codepoint是U+20A0
,其餘相似。一個文本字符串就是這樣一系列的codepoint,表示字符串中每一個字符元素。網絡
固然,你早晚會須要儲存和傳輸這些理論上的Unicode字符串。若是選擇一種其餘人能夠理解的方式以字節方式進行表示,就能夠以你們都理解的方式互相發送文本。這裏就須要引入字符編碼(encoding)。編程語言
字符編碼是在理想的字符和實際的字節表示方法之間的映射。這種映射無需面面俱到,即在某種編碼中也許沒法表示一些特定的字符。同時也無須爲每一個字符使用相同的內存空間,譬如某些字符使用單字節編碼,而其餘字符須要多個字節。函數
因爲同一個字符的字節表現形式不止一種。這意味着當遇到了一串字節,若是不知道使用的是什麼編碼,即便知道這些字節表示的是文本,也不知道是什麼意 思。所能作的就是猜使用的編碼。簡而言之,字節不是文本。即便忘了文中介紹的全部內容,也要記住這句話。爲了讀寫文本,歸根結底就是要知道其中使用的編碼 方式,不論是從約定、標識信息、或是其餘方法得知。編碼
從這裏開始介紹Python的Unicode支持。在Python的類型層次中,有3種不一樣的字符串類型:「unicode」,表示Unicode 字符串(文本字符串)、「str」,表示字節字符串(二進制數據);「basestring」。表示前兩種字符串類型的父類。在我看來,Python在這 裏犯了一個錯誤,根據前面的定義,這讓Python成爲第三類語言,而沒有成爲第四類。code
我用了很長的篇幅苦口婆心的強調字節和字符在本質上是不一樣的東西,只有經過字符編碼才能互相轉換。但不幸的是,Python犯了兩個互不相關的錯誤,輕輕鬆鬆的就會讓你忘掉這些。htm
第一個錯誤的嚴重性值得商榷:即將一串字節視爲字符串。是否應該這樣作還有爭議。Java和,NET認爲這樣作是不對的,而其餘一些語言卻持有相反 的態度。不管如何,你可能但願對文本進行某些操做,如正則匹配、字符串替代等。將這些操做應用到字節序列上都是沒有意義的。而Python將字節序列做爲 另外一種類型的字符串對待,容許在這二者上執行一樣的操做。
第二個錯誤的嚴重性大一些,Python試圖在字節串和字符串之間以不爲人所察覺的方式進行轉化。在不一樣的轉換中,在條件容許的狀況 下,Python會試圖在字節串和unicode字符串直接進行轉換。例如將字節串和unicode字節串鏈接到一塊兒時。根據前面的介紹,不使用 encoding就在不一樣類型之間進行轉換是沒有意義的。因此Python依賴一個「默認編碼」,該編碼由sys.setdefaultencoding()
指定。在大多數平臺上,默認的是ASCII編碼。但對於全部轉換,使用這種編碼幾乎都是錯誤的。若是不手動指定編碼就調用str()
或unicode()
,或是函數以字符串做爲參數,但傳遞的是其餘類型的參數時,都會使用這個默認編碼。
走出這個unicode困境的一個解決辦法是,調用sys.setdefaultencoding()
將默認的編碼設置爲真正會用到的編碼。但這樣僅僅是將問題隱藏起來,雖然這樣剛開始能解決一些文本處理問題。但缺少實際可行性,由於許多應用,特別是網絡應用,在不一樣的地方會使用不一樣的文本編碼。
正確的解決方法是修改代碼,以正確的方式處理文本。下面是一些應該作到的指導性意見:
var.decode(encoding)
(如,var.decode('utf-8')
)。將文本字符串編碼成字節,使用var.encode(encoding)。str()
,也不要在不指定編碼的狀況下就對字節串使用unicode()
。.decode()
將其解釋成文本。一樣,在將文本發送到外部時,老是對文本調用.encode()
。順便說一句,Python 3修復了這些問題,能夠正確的處理unicode和字符串,這樣Python就徹底位於第四類中了,更多信息參見官方的更新說明中關於Unicode的部分。
但願這些內容能幫到你,若是對unicode究竟是什麼,如何處理unicode有疑惑的話,如今應該都清楚了。下次遇到UnicodeEncodeError
或UnicodeDecodeError
錯誤時,就應該徹底知道問題出在哪,也知道如何去修復這些問題!