其實我從來沒寫過需要處理編碼的程式,所以這份筆記我不會用到,只是覺得很有趣所以記下來。
資料來源 http://codeblog.niwyclin.org/posts/196511-unicode-in-python,感謝 wdv4758h 的分享。
ASCII code
定義了 0 ~ 127 的字元
ISO Latin 1 (ISO 8859-1)
定義 0 ~ 255 的字元
重要觀念: Unicode 不是編碼方式,只是把字元編號而已,沒有定義每個字元實際上該怎麼存
例如 U+2602
字元,只代表一個「Unicode 編號 2602(16 進位)」的字元,該怎麼存在電腦裡,隨便
U+2602
被稱為 code point 2602
目前 unicode 已定義 110122 個字元(不含控制字元等等)
並不是「總長度為 16 bit」,而是「一個編碼單位的長度就是 16 bit」
UTF-16 的子集 UCS-2 才是「固定只用 16 bit 編碼」,所以只能表示 0 ~ 65535 的字元
- before U+FFFF
- 在 U+FFFF 以前的字元,就直接用 16 bit 來表示,不足的部份補 0
- after U+FFFF
- 從 U+10000 開始是 16 bit 無法表示的範圍,需要 32 bit 表示,方法如下:
- 把 code point 編號減去 10000(16 進位),會產生 000000 ~ 0FFFFF 的值,總共 20 bit,稱為
a
- 把
a
的前 10 bit 加上D800
(16 進位),稱為a1
,a1
的值會落在 D800 ~ DBFF 之間 - 把
a
的後 10 bit 加上DC00
(16 進位),稱為a2
,a1
的值會落在 DC00 ~ DFFF 之間
- 把 code point 編號減去 10000(16 進位),會產生 000000 ~ 0FFFFF 的值,總共 20 bit,稱為
- Unicode 中的 U+D800 ~ U+DFFF 有被刻意保留不定義任何字元,所以用這種編碼方式表示的資料,每個 byte 都可以單獨分辨是第一或是第二個編碼單位
- 範例: U+1030A
- code point 減去 10000(16),得到 30A,2 進位為 1100001010,不足 20 位元的部分補零,變成 00000000001100001010
- 前 10 位元為 0000000000 ,換成 16 進位為 0000 ,加上 D800 得到 D800
- 後 10 位元為 1100001010 ,換成 16 進位為 030A ,加上 DC00 得到 DF0A
- 得到結果為 U+1030A -> D800DF0A
- 從 U+10000 開始是 16 bit 無法表示的範圍,需要 32 bit 表示,方法如下:
UTF-32 以 32 bit 為一個單位,已經超過 Unicode 的總量,故規則比 UTF-16 簡單,直接把 code point 補成 32 bit 的 binary number
UTF-16 和 UTF-32 的概念簡單,但沒有向下相容,傳統的 ASCII 字元到了 UTF-16/UTF-32 需要增加成 16/32 個 bit,傳統的檔案無法直接開啟,故出現了 UTF-8
UTF-8 讓每個字元都可以分別用不同的長度編碼,如果是傳統的 ASCII 字元,就使用原本的編碼,故傳統 ASCII 編碼的檔案也可以直接開啟
- UTF-8 的解碼方式
- 如果資料的開頭 bit 是 0,表示這個 byte 是 ASCII 編碼的字元
- 如果資料的開頭 bit 是 1,表示這個 byte 是 UTF-8 編碼的一部份,進入以下判斷
- 如果資料的開頭 bit 是 10,表示 byte 是中間的 byte,不是開頭
- 如果資料的開頭 bit 是 110/1110/11110/1*n0,表示這個字元用到 2/3/4/n 個 byte,而且這個 byte 是開頭
- 把以上用來判斷 UTF-8 相關的 bit 去除以後,剩下的 bit 接在一起,組成 code point
- 範例:
00100011 11100101 10100100 10101001
00100011
的開頭是0
,所以它是 ASCII char,查表發現是#
11100101
的開頭是111
,所以這個字元佔了 3 個 byte,這個 byte 的資料部份是00101
10100100
的開頭是10
,由上一步也預測了這點,這個 byte 的資料部份是100100
10101001
的開頭是10
,由上上步也預測了這點,這個 byte 的資料部份是101001
00101
,100100
,101001
組合成00101100100101001
,即為 U+5929 ,查表發現是天
- 解碼
00100011 11100101 10100100 10101001
的結果為#天
Python2
>>> my_unicode_str = u"Hi \u2119\u01b4\u2602\u210c\u00f8\u1f24" >>> len(my_unicode) 9 >>> my_utf8_str = my_unicode_str.encode('utf-8') >>> len(my_utf8) 19 >>> my_utf8 'Hi \xe2\x84\x99\xc6\xb4\xe2\x98\x82\xe2\x84\x8c\xc3\xb8\xe1\xbc\xa4' >>> my_utf8.decode('utf-8') u"Hi \u2119\u01b4\u2602\u210c\u00f8\u1f24"
- Python2 常常會偷轉編碼,很容易壞掉
Python3
- 在 Python3 裡,
str
存的是 Unicode code point,byte
存的是編碼後的字串 - Unicode
str
經過encode()
之後會變成byte
byte
可以decode()
回 Unicodestr
- 在 Python3 裡,