問(wèn)題
最近在用python 2.7寫代碼的時(shí)候叙赚,遇到一個(gè)老生常談的小坑----- ** 字符編碼 **
import MySQLdb
conn = MySQLdb.connect(host=..., user=..., passwd=..., db=..., charset='utf8', port=...) //此處已設(shè)置charset為utf8
從數(shù)據(jù)庫(kù)獲取到的數(shù)據(jù)是:value = '\u6295\u8d44梨水。挤茄。。冰木。穷劈。' ** 注意:前面沒(méi)有u **
然后在django后端views.py中,
from django.http import JsonResponse
用JsonResponse(value)返回?cái)?shù)據(jù)踊沸,前端進(jìn)行渲染歇终,結(jié)果在頁(yè)面上不顯示返回結(jié)果。
排查過(guò)程
- 打開chrome的console調(diào)試逼龟,發(fā)現(xiàn)報(bào)錯(cuò)500--Internal Server Error评凝。。腺律。
- 查看log發(fā)現(xiàn)出現(xiàn)兩種報(bào)錯(cuò):'utf8' codec can't decode bytes in position 3-4: invalid continuation byte 和 'ascii' codec can't decode byte 0xe4 in position 0
- views.py的開頭奕短,已經(jīng)指定了utf-8顯示:
# -*- encoding: utf-8 -*-
importsyswufazhengquejiexi
reload(sys)
sys.setdefaultencoding('utf-8')
調(diào)試(工具為ipython)
查看變量value的類型,可見value并不是unicode類型匀钧,而是str類型
分析
首先我們看下在python中翎碑,unicode 和 utf-8/gb2312/ascii 的區(qū)別 (參照百度百科):
- unicode :在python中是一個(gè)類,函數(shù)unicode(str,"utf8")之斯,將utf8編碼(或其他編碼)的字符串str轉(zhuǎn)換為unicode類的對(duì)象日杈。它表示一個(gè)變量的類型,可以用isinstance(value, unicode)來(lái)查看value變量是否為unicode格式的字符佑刷。
- ascii :是一種字符集莉擒,包括大小寫的英文字母、數(shù)字瘫絮、控制字符等涨冀,翻譯為美國(guó)信息互換標(biāo)準(zhǔn)代碼,是美國(guó)國(guó)家標(biāo)準(zhǔn)學(xué)會(huì)(ANSI)制定的英文編碼麦萤。
- gb2312:國(guó)標(biāo)碼鹿鳖,是對(duì) ASCII 的中文擴(kuò)展
- utf-8:是一種針對(duì)unicode的可變長(zhǎng)度字符編碼扁眯,它是在互聯(lián)網(wǎng)上使用最廣的一種unicode的實(shí)現(xiàn)方式,是為傳輸而設(shè)計(jì)的編碼栓辜,并使編碼無(wú)國(guó)界,這樣就可以顯示全世界上所有文化的字符了
- gb2312和utf-8都是按照unicode規(guī)定的方式垛孔,用不同的編碼排列方式藕甩,將字符與二進(jìn)制的01進(jìn)行對(duì)應(yīng)的表。
然后我們研究下python中的encode和decode函數(shù):
unicode_1 = u'中文'
str_1 = unicode_1.encode('gb2312')
將unicode對(duì)象unicode_1用gb2312編碼周荐,得到str類型變量str_1狭莱,結(jié)果見圖:
unicode_2 = str_1.decode('gb2312')
用gb2312編碼對(duì)字符串str_gb進(jìn)行解碼,得到unicode類型對(duì)象概作,結(jié)果見圖:
decode()和encode()關(guān)系如圖:
解決辦法
現(xiàn)在回到我們的問(wèn)題中腋妙,查看了一下mysql數(shù)據(jù)庫(kù),發(fā)現(xiàn)里面存儲(chǔ)的數(shù)據(jù)確實(shí)是utf-8格式讯榕,因此用網(wǎng)上搜到的方法
value = value.decode('gb2312').encode('utf-8')
來(lái)修改代碼骤素,前端仍然不顯示。這里是因?yàn)閿?shù)據(jù)庫(kù)里存儲(chǔ)的并非gb2312格式的數(shù)據(jù)愚屁,所以decode是沒(méi)用的济竹。
再查,發(fā)現(xiàn)產(chǎn)生 ‘XXX' codec can't decode bytes in position XX 這種錯(cuò)誤信息的真正原因霎槐,竟然是decode過(guò)程中遇到了非法字符送浊,因此無(wú)法正確解碼(參照:blog.csdn.net/cnmilan/article/details/9264643)。
而在做decode時(shí)丘跌,加上ignore參數(shù)袭景,則會(huì)忽略非法字符:
value = value.decode('utf-8', 'ignore').encode('utf-8')
至此,問(wèn)題得到解決闭树。
總結(jié)
一定要真正理解unicode和utf-8的區(qū)別耸棒,還有要梳理清楚decode()和encode()的關(guān)系,排查出問(wèn)題的真正原因报辱,而不是隨便在網(wǎng)上搜索模糊的答案榆纽,一來(lái)未必能真正解決問(wèn)題,二來(lái)永遠(yuǎn)不會(huì)進(jìn)步捏肢!