編碼/解碼問題是個大坑缰儿,其中的復雜性频祝,大多來自歷史包袱
由于計算機領域的分層架構和多平臺問題沧踏,這個問題被進一步加劇尤泽,unicode的出現(xiàn)欣簇,給這個問題帶來了曙光.
可是生活不總是那么美好的,有些時候坯约,一不小心熊咽,我們還是會掉到滿是泥沼的坑里
周末在鳳凰書城看一本數(shù)據(jù)清洗相關的書,其中說道噪聲數(shù)據(jù)的問題闹丐,有問題的編碼是噪聲的來源之一横殴,書中分享了不少好用的方法,在便簽里記了一些卿拴,加上之前筆記里的衫仑,正好整理成一篇文章
編碼問題
這里引《中文編碼雜談》中關于亂碼的討論
在Linux平臺上如果使用cat等命令查看文件中的中文內容時梨与,可能出現(xiàn)亂碼。這也是編碼的問題惑畴。簡單的說是文件時按照A編碼保存蛋欣,但是cat命令按照當前Locale設定的B編碼去查看航徙,在B和A不兼容的時候就出現(xiàn)了亂碼如贷。
核心概念
字符集
簡單的說字符集就規(guī)定了某個文字對應的二進制數(shù)字存放方式(編碼)和某串二進制數(shù)值代表了哪個文字(解碼)的轉換關系
字符編碼
字符編碼(英語:Character encoding)、字集碼是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式到踏、自然數(shù)序列杠袱、8位組或者電脈沖),以便文本在計算機中存儲和通過通信網絡的傳遞窝稿。常見的例子包括將拉丁字母表編碼成摩斯電碼和ASCII楣富。其中,ASCII將字母伴榔、數(shù)字和其它符號編號纹蝴,并用7比特的二進制來表示這個整數(shù) --wikipedia
Unicode和UTF-8
Unicode對世界上大部分的文字系統(tǒng)進行了整理、編碼踪少,使得電腦可以用更為簡單的方式來呈現(xiàn)和處理文字塘安。
Unicode只是一個符號集,它只規(guī)定了符號的二進制代碼援奢,卻沒有規(guī)定這個二進制代碼應該如何存儲
而UTF-8就是字符編碼兼犯,是Unicode規(guī)則字庫的一種實現(xiàn)形式
Python中的編碼問題
Python編碼和Unicode一文中描述了python中可能出現(xiàn)的一些編解碼難題
原因之一是Python 2.x默認將所有的字符串當做ASCII來對待(python3中會好很多)
當你使用string類型時,實際上會儲存一個字節(jié)串
解碼字節(jié)流
你可以把字節(jié)流解碼(decode)成一個Unicode對象集漾,把一個Unicode 對象編碼(encode)為字節(jié)流
你最好是盡早的將字節(jié)流解碼為Unicode(字節(jié)流進入程序的時候)
你不能簡單地輸出一個Unicode對象切黔。它必須在輸出前被變成一個字節(jié)串
str/unicode
在mac下python2.7.5
# unicodeunicode_a=u"你好"type(unicode_a)# unicode_a# u'\u4f60\u597d'unicode_b=u'\u4f60\u597d'unicode_b# u'\u4f60\u597d'unicode_a==unicode_b# True# unicode_c = unicode("你好") python2會報錯,python會把所有東西作為字節(jié)流理解u'hello'==unicode("hello")#? True具篇,如果unicode_a.encode("utf-8")# '\xe4\xbd\xa0\xe5\xa5\xbd'################# strstr_a="你好"str_a# '\xe4\xbd\xa0\xe5\xa5\xbd'str_a.decode("utf-8")# u'\u4f60\u597d'str_a.decode("utf-8")==u"你好"# True
__repr__的目標是準確性纬霞,__str__的目標是可讀性
codecs模塊
codecs模塊能在處理字節(jié)流的時候提供很大幫助。你可以用定義的編碼來打開文件并且你從文件里讀取的內容會被自動轉化為Unicode對象驱显。
讀寫文件
當從一個文件讀取數(shù)據(jù)的時候险领,codecs.open 會創(chuàng)建一個文件對象能夠自動將utf-8編碼文件轉化為一個Unicode對象,而寫入文件這樣寫:
importcodecsfh=codecs.open("/tmp/utf-8.txt","w","utf-8")fh.write(u"\u2013")fh.close()
使用urllib流
stream=urllib.urlopen("http://www.google.com")Reader=codecs.getreader("utf-8")fh=Reader(stream)type(fh.read(1))# ,應該盡可能讓程序內部的數(shù)據(jù)都是
你必須對codecs模塊十分小心。你傳進去的東西必須是一個Unicode對象,否則它會自動將字節(jié)流作為ASCII進行解碼秒紧。
策略
python編解碼涉及的問題可能很多绢陌,上至大神下至小白,都可能受擾熔恢,為了保持簡單脐湾,我們可以保持這樣一種習慣:關注輸入輸出,內部保持unicode叙淌。
每當有數(shù)據(jù)進入程序秤掌,將其解碼(decode)為unicode(utf-8)
當有數(shù)據(jù)從程序中輸出時愁铺,將其編碼(encode)為utf-8
最佳實踐:
最先解碼(解碼為unicode對象),最后編碼(輸出為字節(jié)碼)
最先解碼意味著無論何時有字節(jié)流輸入闻鉴,需要盡早將輸入解碼為Unicode
最后編碼意味著只有你打算將文本輸出到某個地方時茵乱,才把它編碼為字節(jié)流。這個輸出可能是一個文件孟岛,一個數(shù)據(jù)庫瓶竭,一個socket等等
默認使用utf-8編碼
使用codecs和Unicode對象來簡化處理
codecs模塊能夠讓我們在處理諸如文件或socket這樣的流的時候能少踩一些坑。如果沒有codecs提供的這個工具渠羞,你就必須將文件內容讀取為字節(jié)流斤贰,然后將這個字節(jié)流解碼為Unicode對象。
codecs模塊能夠讓你快速的將字節(jié)流轉化為Unicode對象次询,省去很多麻煩荧恍。
linux下的一些工具
file
file命令用來探測給定文件的類型,
參數(shù):
-i:顯示MIME類別。
-c:詳細顯示指令執(zhí)行過程屯吊,便于排錯或分析程序執(zhí)行的情形送巡;
file *
iconv
conv命令是用來轉換文件的編碼方式的,比如它可以將UTF8編碼的轉換成GB18030的編碼
iconv -f encoding -t encoding inputfile
iconv -f UTF-8 -t GBK file1 -o file2 //將一個UTF-8 編碼的文件轉換成GBK編碼
chardet
有時候我們不知道文件/字節(jié)流采用了什么編碼盒卸,可以讓chardet來猜測編碼骗爆,chardet是python的一個庫
附
傳輸編碼語法(transfer encoding syntax)
用于處理上一層次的字符編碼方案提供的字節(jié)序列。一般其功能包括兩種:一是把字節(jié)序列的值映射到一套更受限制的值域內世落,以滿足傳輸環(huán)境的限制淮腾,例如Email傳輸時Base64或者quoted-printable,都是把8位的字節(jié)編碼為7位長的數(shù)據(jù)屉佳;另一是壓縮字節(jié)序列的值谷朝,如LZW或者進程長度編碼等無損壓縮技術。
BASE64編碼
Base64常用于在通常處理文本數(shù)據(jù)的場合武花,表示圆凰、傳輸、存儲一些二進制數(shù)據(jù)
常用于在URL体箕、Cookie专钉、網頁中傳輸少量二進制數(shù)據(jù)。
所謂Base64累铅,就是說選出64個字符----小寫字母a-z跃须、大寫字母A-Z、數(shù)字0-9娃兽、符號"+"菇民、"/"(再加上作為墊字的"=",實際上是65個字符)----作為一個基本字符集。然后第练,其他所有符號都轉換成這個字符集中的字符阔馋。
關于base64更多的細節(jié)可以參考維基百科和Base64筆記
在python中實現(xiàn)
importbase64base64.b64encode('hello')#aGVsbG8=? , 被編碼的不是應該是二進制數(shù)據(jù)吧娇掏,python的二進制默認被解析為ascii呕寝?,在這里hello無論是b'hello'還是'hello'/u'hello'婴梧,結果都一樣base64.b64decode("aGVsbG8=")# helloa=u'你好'# \u4f60\u597db=base64.b64encode(a.encode("utf-8"))#5L2g5aW9c=base64.b64decode(b)# \xe4\xbd\xa0\xe5\xa5\xbdc.decode("utf-8")# u'\u4f60\u597d'# 由于標準的Base64編碼后可能出現(xiàn)字符+和/下梢,在URL中就不能直接作為參數(shù),所以又有一種"url safe"的base64編碼志秃,其實就是把字符+和/分別變成-和_d=u"哈里謝頓".encode("utf-8")# \xe5\x93\x88\xe9\x87\x8c\xe8\xb0\xa2\xe9\xa1\xbfbase64.b64encode(d)# 5ZOI6YeM6LCi6aG/ , 有反斜杠e=base64.urlsafe_b64encode(d)# 5ZOI6YeM6LCi6aG_base64.urlsafe_b64decode(e)# \xe5\x93\x88\xe9\x87\x8c\xe8\xb0\xa2\xe9\xa1\xbf
url中的編碼問題
一般而言使用urllib庫中的urlencode函數(shù)就好了
fromurllibimporturlencodedata={'name':'小明',#u'小明'則需要encode為字節(jié)碼'age':'10歲'}printurlencode(data)# age=10%E5%B2%81&name=%E5%B0%8F%E6%98%8E#僅對字符串進行轉碼可以使用quotefromurllibimportquotequote('小明')#%E5%B0%8F%E6%98%8E'
如果有大量此類工作可以考慮使用furl
在線工具
Unicode編碼轉換:還包括unix時間戳
一些細碎知識
Windows簡體中文版中怔球,ANSI是默認的編碼方式嚼酝。對于英文文件是ASCII編碼浮还,對于簡體中文文件是GB2312編碼(繁體中文版會采用Big5碼)
python中字節(jié)流和unicode的代碼視角比較
stackoverflow Python unicode equal comparison failed