字符編碼知識梳理

[toc]

字符編碼:

字符編碼(英語:Character encoding)、字集碼是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式、自然數序列、8位組或者電脈沖),以便文本在計算機中存儲和通過通信網絡的傳遞。

字符編碼的種類和進化流程

注意:上圖中的擴展ascii表和gb3212等編碼方式到unicode編碼方式中間的箭頭,僅僅表示編碼的歷史進化,而不是unicode來自gbk等編碼方式

常用轉換關系:
gbk<=======>unicode
utf-8<=======>unicode

就是通過以上的轉換,將GBK編碼方式的文件,轉換成UTF-8格式的

在哪里需要字符編碼的轉換?

了解了字符編碼的種類之后,我們需要了解,在什么地方,使用什么樣的編碼,以及為什么要使用這些編碼方式。

輸入保存至計算機流程

上圖表示了計算機解讀和存儲我們在編輯器中輸入字符的過程??赡苤虚g部分的表述并不是很準確(也可以是gbk,主要取決于編輯器的編碼方式),但是能大致的說明“明文————>高低電平”的轉換過程。

在這里我們需要特別注意的是文本編輯器存儲到內存過程中,是不是內存中只能存在unicode方式的編碼。

解釋器讀寫python文件流程

python默認的編碼

  1. python2 默認使用ascii

  2. python3 默認使用utf-8

兩種python的數據類型以及對應的存入數據

數據類型:存入數據(默認編碼為xxxx的二進制數)

  • python2.x:
    1.str(字符串):bytes
    2.unicode(字節串):unicode

  • python3.x:
    1.str(字符串):unicode
    2.bytes(字節串):bytes

首先解決一下“什么是字節串?”這個問題:
python2.x中str的類型存入數據為bytes(這也是為什么在出現python2的unicode錯誤時,在原定義的字符串前添加一個‘u’,就有可能解決問題)。bytes代表的是(二進制數的序列),通過ascii編碼后才是我們看到的字符形式,如果我們單獨取出一個字節,可以發現它的切片是一個數字
print(b'12ffse'[0])

由此也可以推出,字節類型只能允許ascii字符?

簡單來說就是把人類通用的語言符號翻譯成計算機通用的對象,而反向的翻譯過程自然就是解碼了。Python 中的字符串類型代表人類通用的語言符號,因此字符串類型有encode()方法;而字節類型代表計算機通用的對象(二進制數據),因此字節類型有decode()方法

為什么說python3規范了默認str的字符編碼是unicode使編碼更加規范方便了呢?
可以參考一下上方的unicode和utf8以及gbk之間的轉換關系。
同時測可以測試一下以下代碼:
print(type('hello'+u'world'))
(在python2環境中運行)

相同的方式,在python3中可以這樣實驗:
print('hello'+b'world')

這個實驗的目的就是:在不同的兩個解釋器中,拼接兩個不同編碼類型的字符串。
從而可以看出python2在字符編碼上的“寬容”容易造成一些未知錯誤。而python3在字符編碼方面的嚴格要求,決定了程序運行的規范和穩定性

更多關于字符編碼理解的實驗:http://python.jobbole.com/84839/

小結:

文本編輯器的編碼,由文本編輯器的設置決定

語言解釋器的解碼方式,有語言解釋器的默認設置和打開代碼的抬頭(python解釋器默認設置第一二行能有效定義解碼方式;#coding:UTF-8

如果說,語言解釋器的解碼方式和文本編輯器的編碼不同,就會出現錯誤。

如果說,文本編輯器打開文件的編碼和文件存儲定義的解碼方式不同,也會出現錯誤

說到錯誤,上面鏈接的作者在git上有一篇關于python中unicode正確使用方法的文章https://github.com/rainyear/pytips/blob/master/Markdowns/2016-03-17-Bytes-decode-Unicode-encode-Bytes.md ,我覺得里面例子很有意義,所以下面就是照著他的方式敲一遍實驗代碼用于明確python中的編碼錯誤。

python 中的unicode的正確用法

python編碼中最常見的兩個錯誤

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 0-1: invalid continuation byte

encode和decode

encode和decode都是翻譯的過程,而ascii和unicode在本質上都像是一本將人類語言和就算計語言對應起來的字典。而且這兩本本字典有厚有薄,ASCII 只包含了26個基本拉丁字母、阿拉伯數目字和英式標點符號一共128個字符,因此只需要(不占滿)一個字節就可以存儲,而 Unicode 則涵蓋的數據除了視覺上的字形、編碼方法、標準的字符編碼外,還包含了字符特性,如大小寫字母,共可包含 1.1M 個字符,而到現在只填充了其中的 110K 個位置。

unicode error實驗

利用一個函數來展現上面的兩個錯誤

encode error實驗

def try_encode(s, encoding = 'utf-8'):
    try:
        print(s.encode(encoding))
    except UnicodeEncodeError as err:
        print(err)
# 定義一個測試字符串默認編碼的函數,傳入默認解碼方式為utf-8

s = '4b'
try_encode(s)
try_encode(s,'ascii')
# 使用ascii的方式解碼

b = '城'
try_encode(b)
try_encode(b,'ascii')
try_encode(b,'GB2312')

反饋結果:

b'4b'
b'4b'
b'\xe5\x9f\x8e'
'ascii' codec can't encode character '\u57ce' in position 0: ordinal not in range(128)
b'\xb3\xc7'

結果分析:
由于 UTF-8 對 ASCII 的兼容性,"4b" 可以用 ASCII 進行編碼;而 "城"不能使用ascii編碼,因為它已經超出了 ASCII 字符集的 128 個字符,所以引發了 UnicodeEncodeError;而 "城" GB2312 中的碼位是 b'\xd3\xea',與 UTF-8 不同,但是仍然可以正確編碼。因此如果出現了 UnicodeEncodeError 說明你用錯了字典,要翻譯的字符沒辦法正確翻譯成碼位!

decode error實驗

def try_decode(s, decoding = 'utf-8'):
    try:
        print(s.decode(decoding))
    except UnicodeDecodeError as err:
        print(err)

s = b'4b'
try_decode(s)
try_decode(s,'ascii')

b = b'\xb3\xc7'
try_decode(b)
try_decode(b,'ascii')
try_decode(b,'GB2312')
try_decode(b,'GBK')
try_decode(b,'Big5')

try_decode(b.decode('GB2312').encode())
# byte - decode - unicode - encode - byte

反饋結果:

4b
4b
'utf-8' codec can't decode byte 0xb3 in position 0: invalid start byte
'ascii' codec can't decode byte 0xb3 in position 0: ordinal not in range(128)
城
城
傑
城

一般后續出現的字符集都是對 ASCII 兼容的,可以認為 ASCII 是他們的一個子集,因此可以用 ASCII 進行解碼(編碼)的,一般也可以用其它方法;對于不是不存在子集關系的編碼,強行解碼有可能會導致錯誤或亂碼!

實踐中的注意事項:

  1. 記清楚編碼和解碼的方向

  2. 在python中盡量采用utf-8,輸入或者輸出的時候根據需求確定是否需要編碼成二進制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容