今天臺風(fēng)過境瓮恭,風(fēng)雨交加,突然就想寫一下這個(gè)在學(xué)習(xí)編程初期产还,非常困擾的問題匹厘。
我會盡我所能將這個(gè)問題描述清楚,如果有不當(dāng)之處還請指正雕沉!
轉(zhuǎn)載請注明出處即可
字符與字節(jié)
字符 就是我們平臣牵看得見的這些,比如 '1'坡椒,'c',"中文"尤溜, 在程序代碼中它們往往表現(xiàn)為一個(gè)字符串(其實(shí)字符的范圍要廣得多倔叼,還包括不可見的控制字符,后來出現(xiàn)的 emoj 字符等)
但是我們知道其實(shí)計(jì)算機(jī)內(nèi)部都是只含 01 的二進(jìn)制序列宫莱,于是又引出了兩個(gè)概念
字節(jié) 丈攒,比如 \x31, \xff,它們才是實(shí)際在計(jì)算機(jī)中存儲的東西
而從字符到字節(jié)的映射巡验,就是 字符編碼
ASCII 編碼
這是我們經(jīng)常聽到的編碼际插,在 ASCII 編碼中,可見字符 '1','2','a','f' 或者 控制字符 '\n','\b' 等都被編碼為一個(gè)字節(jié)
其他編碼
其他編碼多種多樣显设,比如 GBK框弛,GB2312 等,它們可以表示漢字捕捂,將每個(gè)漢字編碼為多個(gè)字節(jié)
所以也就出現(xiàn)了所謂 多字節(jié)字符 的概念
使用范圍最廣的應(yīng)該是 UTF-8 編碼瑟枫,它用 1-6 的字節(jié)來編碼世界上所有的字符,包括拉丁字符指攒,中文字符慷妙,西洋字符,emoj 字符等等允悦,幾乎所有
編碼報(bào)錯(cuò)
了解了這些就能理解為什么 編碼出錯(cuò)膝擂,解碼出錯(cuò) 等概念了,亂碼 也是一樣的道理
哪些地方會出現(xiàn)編碼問題呢隙弛?我們分幾個(gè)場景來闡述
- 記事本(文本編輯器)
當(dāng)我們將一堆字符敲完了架馋,保存時(shí)文本編輯器一般都會有提示,你想以什么格式保存驶鹉?
舉個(gè)栗子會比較直觀: 我們敲入了 '中文'
绩蜻,保存時(shí)選擇 UTF-8 編碼,那么計(jì)算機(jī)內(nèi)部就存儲一個(gè) '\xe4\xb8\xad\xe6\x96\x87'
室埋,如果選擇 GBK 編碼办绝,那么就存儲一個(gè) '\xd6\xd0\xce\xc4'
,這是編碼的過程
相應(yīng)的我們關(guān)閉編輯器后重新打開姚淆,可以選擇以什么編碼格式來打開孕蝉,這就是解碼的過程
所以如果編碼方式和解碼方式不一致,自然就無法將字節(jié)('\xe4\xb8\xad\xe6\x96\x87'
)轉(zhuǎn)為 我們期望的字符('中文'
)
了解清楚了么腌逢,然后我們開始擴(kuò)展到更多情景
ps: 不要用記事本寫代碼降淮,容易編碼出錯(cuò)是一方面,主要顯得太 low 了
- 我們將字符串賦值給一個(gè)變量搏讶,然后輸出到屏幕
這里除了要保證源代碼文件保存時(shí)的編碼和重新打開時(shí)的編碼一致之外佳鳖,多了一點(diǎn)需要注意
當(dāng)程序運(yùn)行起來的時(shí)候,程序內(nèi)部也有自己的編碼方式(wtf?)媒惕,這里以 python2 為例
python2 內(nèi)部默認(rèn)格式是 unicode 系吩,然而它也允許另外一種格式 str,em...
>>> s = '中文'
>>> s
'\xe4\xb8\xad\xe6\x96\x87'
>>> type(s)
<type 'str'>
可以看到這就是我們的字符串它的類型是 str妒蔚,編碼方式是 UTF-8
所以我們用 UTF-8 來解碼一下穿挨,嗯月弛,得到了一個(gè) unicode 類型,真不錯(cuò)
>>> s.decode('utf-8')
u'\u4e2d\u6587'
>>> type(s.decode('utf-8'))
<type 'unicode'>
python 一定也是這么做的吧
no... 在 python2 中科盛,默認(rèn)使用 ascii 格式來處理的
也就是說帽衙,當(dāng) python2 檢測到該字符串不是 unicode 類型,就嘗試用 ascii 去解碼贞绵,即 s.decode('ascii')
相應(yīng)的厉萝,在輸出的時(shí)候,檢測到該字符串不是 str 類型但壮,就嘗試用 ascii 去編碼
所以你就遇到了這樣兩個(gè)錯(cuò)誤
UnicodeEncodeError: 'ascii' codec can't encode characters
UnicodeDecodeError: 'ascii' codec can't decode characters
對于這個(gè)編碼轉(zhuǎn)換發(fā)生在什么時(shí)候我不太清楚冀泻,知道的請告訴我!
不過這樣下去總不是個(gè)事兒
最好的解決方法就是遷移到 python3 吧(強(qiáng)烈推薦)
python3 統(tǒng)一了字符串表示用 str蜡饵,內(nèi)部存儲用的是 bytes弹渔,默認(rèn)編碼格式是 UTF-8!
更多信息自己去查詢溯祸,這里有點(diǎn)跑偏了
- 我們?yōu)g覽網(wǎng)頁的時(shí)候
與上述情況類似肢专,數(shù)據(jù)在網(wǎng)絡(luò)上傳播的形式是 比特流/字節(jié)流,這就意味著還是要編碼
存儲在服務(wù)器的網(wǎng)頁信息(字符)焦辅,在服務(wù)端編碼變成比特流/字節(jié)流博杖,然后我們的瀏覽器拿到這些比特流/字節(jié)流,解碼成(我們實(shí)際看到的)網(wǎng)頁(字符)
編碼解碼不一致就又會亂碼啦筷登!
不過一般網(wǎng)頁都會指定編碼方式 <meta charset="utf-8"/>
瀏覽器也會按指定格式解碼剃根,一般沒事
出錯(cuò)了調(diào)整下瀏覽器編碼方式試試
結(jié)語
差不多就這么多了,點(diǎn)個(gè)關(guān)注嘛..