在實踐中,很多初學者都遇到過“檔案顯示亂碼”的情況,其多數都是由於在開啟檔案時,沒有選對編碼格式導致的。因此,學習 Python 中的字元或字串,了解其底層的編碼格式是非常有必要的。
鑑於有些讀者並不了解什麼是編碼格式,本節先從編碼開始講起。
什麼是編碼?
雖然很多教學中有關於編碼的定義,但對初學者來說並不容易理解,這裡先舉一個例子。古代打仗,擊鼓為號、鳴金收兵,即把要傳達給士兵的命令對應為公認的其他形式,這就和編碼有相似之處。
以發布進攻命令為例,相比用嗓子喊,敲鼓發出的聲音傳播的更遠,並且士兵聽到後也不會引起歧義,因此長官下達進攻命令後,傳令員就將此命令轉化為對應的鼓聲,這個轉化的過程稱為編碼;由於士兵都接受過訓練,聽到鼓聲後,他們可以將其轉化為對應的進攻命令,這個轉化的過程稱為解碼。
需要說明的是,此例只是形象地描述了編碼和解碼的原理,真實的編碼和解碼過程比這要複雜的多。
了解了編碼的含義之後,接下來再介紹一下字元編碼。
什麼是字元編碼?
我們知道,計算機是以二進位制的形式來儲存資料的,即它只認識 0 和 1 兩個數位。 20 世紀 60 年代,是計算機發展的早期,這時美國是計算機領域的老大,它制定了一套編碼標準,解決了 128 個英文字元與二進位制之間的對應關係,被稱為 ASCII 字元編碼(簡稱 ASCII 碼)。
ASCII 碼,全稱為美國資訊交換標準程式碼,是基於拉丁字母的一套字元編碼,主要用於顯示現代英語,因為全球資訊網的出現,使得 ASCII 碼廣為使用,其直到 2007 年 12 月才逐漸被 Unicode 取代。
雖然英語用 128 個字元編碼已經夠用,但計算機不僅僅用於英語,如果想表示其他語言,128 個符號顯然不夠用,所以很多其他國家都在 ASCII 的基礎上發明了很多別的編碼,例如包含了漢語簡體中文格式的 GB2312 編碼格式(使用 2 個位元組表示一個漢字)。
也正是由於出現了很多種編碼格式,導致了“檔案顯示亂碼”的情況。比如說,傳送郵件時,如果發信人和收信人使用的編碼格式不一樣,則收信人很可能看到亂碼的郵件。基於這個原因,Unicode 字元集應運而生。
Unicode 字元集又稱萬國碼、國際碼、統一碼等。從名字就可以看出來,它是以統一符號為目標的字元集。Unicode 對世界上大部分的文字系統進行了整理、編碼,使得電腦可以用更簡單的方式來呈現和處理文字。
注意,在實際使用時,人們常常混淆字元集和字元編碼這兩個概念,我認為它們是不同的:
-
字元集定義了字元和二進位制的對應關係,為每個字元分配了唯一的編號。可以將字元集理解成一個很大的表格,它列出了所有字元和二進位制的對應關係,計算機顯示文字或者儲存文字,就是一個查表的過程;
-
而字元編碼規定了如何將字元的編號儲存到計算機中,要知道,有些字元編碼(如 GB2312 和 GBK)規定,不同字元在儲存時所佔用的位元組數是不一樣的,因此為了區分一個字元到底使用了幾個位元組,就不能將字元的編號直接儲存到計算機中,字元編號在儲存之前必須要經過轉換,在讀取時還要再逆向轉換一次,這套轉換方案就叫做字元編碼。
Unicode 字元集可以使用的編碼方案有三種,分別是:
-
UTF-8:一種變長的編碼方案,使用 1~6 個位元組來儲存;
-
UTF-32:一種固定長度的編碼方案,不管字元編號大小,始終使用 4 個位元組來儲存;
-
UTF-16:介於 UTF-8 和 UTF-32 之間,使用 2 個或者 4 個位元組來儲存,長度既固定又可變。
其中,UTF-8 是目前使用最廣的一種 Unicode字元集的實現方式,可以說它幾乎已經一統江湖了。
Python使用哪種字元編碼?
了解了什麼是編碼,以及什麼是字元編碼之後,最後解決“Python 使用哪種字元編碼?”這個問題。
Python 3.x 中,字串採用的是 Unicode 字元集,可以用如下程式碼來檢視當前環境的編碼格式:
>>> import sys
>>> sys.getdefaultencoding()
'utf-8'
同時,在 Python 3.x 中也可以用 ord() 和 chr() 函數實現字元和編碼數位之間的轉換,例如:
>>> ord('Q')
81
>>> chr(81)
'Q'
>>> ord("網")
32593
>>> chr(32593)
'網'
Python 2.x 中無法使用 ord() 得到指定字元對應的編碼數位。
由此可以知道,在 Unicode 字元集中,字元‘Q’對應的編碼數位為 81,而中文‘網’對應的編碼數位為 32593。
值得一提的是,雖然 Python 預設採用 UTF-8 編碼,但它也提供了 encode() 方法,可以輕鬆實現將 Unicode 編碼格式的字串轉化為其它編碼格式。有關 encode() 方法的用法,可閱讀《Python encode()和decode()方法》 一節。