2021-01-18更新
參考鏈接:https://www.bilibili.com/video/BV1Sp4y1U7Jr?p=160
最近一直空的時候看一些老男孩的教學視頻,對基礎的Python知識查漏補缺。
剛看到字符的解碼與編碼問題,記錄一些我自己的學習感悟吧。
當我們在終端輸出看到的,包括一些文本編輯器看到的,理論上面都是Unicode的字符串形式。
當我們打開一個文本文件的時候,必須用一直指定的編碼形式來解碼文件中的數據,你可以用各種不同的編碼集,但正確的應該是你寫入硬盤的時候的編碼集。
說到底,文字還是給人看的,計算機最終的保存還是二進制的文件,在20世紀90年代,為了創造一種能包含全球文字的字符集出現了,那就是Unicode。
計算機啟動后,內存中應該運行這個Unicode字符集的類似與mapping的程序,這是一個大型的中轉站,因為現在的計算機必須要基本的需求,能夠顯式各種文字。
所以Unicode的每一個字都又對應的不同字符集的對應的編碼,可以理解為Unicode的每一個字為key,不同的字符集的編碼為value,而且是一對多的。
好比一個中文字"好"的Unicode編碼為'\u597d',他針對不同的字符集有着不同的對應編碼。
我說的是假如:GBK下面對應的是\x1122,這樣的話,如果通過GBK的方式保存在電腦的話數據就是\x1122,但打開的時候通過GBK解碼,會找到對應Unicode的編碼,從而顯式正常的數值。
按照現在的趨勢來看,后續的任何文本操作都應該保存為utf8格式,因為utf8是Unicode的一種轉換表達,簡單理解utf8與Unicode的編碼存在一一對應的關系。
但另外的字符集可能就會出現問題,中國的字符集不能編碼日文,從而不能保存有日文的信息。
對於不能理解字符集相關知識的朋友,記住一點就夠了,就是從現在開始,任何的文本文件都用utf8進行操作與保存就對了。在計算機內部Unicode的暫存是因為給另外的編碼集當中轉站使用。
學了不少時間了,對於字符串的編碼與解碼總算有了一些認識。
記錄一些自己的想法。
首先,在Python3里面,我們的str類型,相當於Python2的中unicode。
>>> name = '四店'
>>> name
'\xe5\x9b\x9b\xe5\xba\x97'
>>> uname = name.decode('utf-8')
>>> print name
四店
>>> print uname
四店
>>> uname
u'\u56db\u5e97'
>>> type(uname)
<type 'unicode'>
>>> type(name)
<type 'str'>
上面的是在python2中運行的結果,可以看到終端模式下,輸入字符串對象只會出來對象的字節碼數據,只有在print輸出的時候,才出現對應的文字。
但在Python3中完全不一樣了,py3的str就是py2的unicode
In [663]: name = u'\u56db\u5e97'
In [664]: name
Out[664]: '四店'
In [665]: uname = name.encode('utf8')
In [666]: uname
Out[666]: b'\xe5\x9b\x9b\xe5\xba\x97'
In [667]: print(name)
四店
In [668]: print(uname)
b'\xe5\x9b\x9b\xe5\xba\x97'
在這里可以看到,我們賦值給name的unicode的字符標識,但無論是終端還是print輸出,name一直是四點了。
name明顯是str模式,所以證明了前面說的。
后面流暢的Python我會記錄自己的更加詳細對與解碼,編碼的理解。
其實想通了感覺也就那么回事,網上好多資料,基本都是一大抄。
我最后稍微總結一下。unicode就是一份超級大字典,全世界所有的文字都有,所以Python終端顯示用unicode蠻好
但unicode的對應表,很多人覺的傳輸數據的時候不好,就搞出來了utf-8,utf-16,utf-32,這個也是一份映射表。
假如在unicode里面,每個文字的名字相當與碼位,utf-8,utf-16就好比編號。
比如"好"的unicode為u'\u597d',utf-8為'\xe5\xa5\xbd'
這個一對一對應的,就是說,unicode里面的每一個字符都可以找到utf-8的對應字節序(通俗講就是編號)。
所以大家應該用utf-8。
再來說一些地方與國家的編碼,比如中國的gbk,gbk包含了全部的中國字與字節碼的映射關系,可以想象成這是一本,中國的地方字典。
In [669]: name Out[669]: '四店' In [670]: bytes(name,encoding='gbk') Out[670]: b'\xcb\xc4\xb5\xea'
當我們在encode的時候,首先先拿gbk對照表里面的字來對比需要解碼的字,比如第一個四字,找到了它的序號為\xcb\xc4,然后對編號進行輸出。
所以這個很容易造成問題,你如果給一個他字符集沒有的字,找了老半天才找到。
In [679]: '\u20ac'.encode('gbk')
---------------------------------------------------------------------------
UnicodeEncodeError Traceback (most recent call last)
<ipython-input-679-90f6ff568b6b> in <module>
----> 1 '\u20ac'.encode('gbk')
UnicodeEncodeError: 'gbk' codec can't encode character '\u20ac' in position 0: illegal multibyte sequence
In [680]: '\u20ac'
Out[680]: '€'
就報錯了,話說gbk的字符集還是蠻厲害了,輸入了日本字,意大利的字,基本都有。
但對於utf-8只要是unicode的字符,他都能編碼
In [680]: '\u20ac'
Out[680]: '€'
In [681]: '\u20ac'.encode('U8')
Out[681]: b'\xe2\x82\xac'
所以再次推薦大家以后都用utf-8這樣的話,以后里面的decode,encode的ignore參數都可以不用了。
這里還有一份老外寫的參考鏈接:https://pycoders-weekly-chinese.readthedocs.io/en/latest/issue5/unipain.html
還行吧,反正我沒咋看懂,應該有很多py2的說明,py2基本沒啥節操過。
前面寫的我就不刪除了,從流暢的Python書中指出,Python內置100多種字符編碼。
我的理解就是,當給你一個字符假如是"我",Python的100多種解碼器中能找到"我"的對應的字節序列是不一樣的。
你通過編碼的方式把unicode中的"我"轉換成字節序列,可以是2字節,也可能是3字節。
可以用於計算機的存儲,或者網絡的傳輸,但機器看的懂的,人看不懂,當需要人看的時候,你又需要用同樣的方式把機器的字節序列裝換成人看的懂的。
這個就是解碼。你用什么編碼的,就必須用什么解碼,其實有了utf8,另外的編碼方式真的應該淘汰了。
解碼以后就是unicode了,你可以把unicode想象成就是文字。
但unicode變成文字是在哪里實現,又是如何實現的,網上的資料比較少。所以我只能把unicode想象成文件了,
因為你編碼的時候就是對unicode進行編碼。
2020年12月4日補充
一段時間的學習與成長,Python網絡編程書中說到Python的字符串(character)包含了Unicode字符。以前一直對這個不是很懂,很多書籍解釋了,可能是翻譯問題,或者我理解問題,不能很好的理解Unicode與Python中的字符串的意思。我現在的理解,就是Python的字符串,就是一堆Unicode。當你在Python的編輯器環境下,只要看到了字符串內有\u形式的字符,就會通過Unicode字符表找到對應碼點(code point)的字符
這里一些記錄:https://www.zhihu.com/question/26921730/answer/1610972540
