一文教你解決所有Python中文亂碼問題
前言
最近偶爾會幫忙寫一些爬蟲代碼,有一些需要使用 Python 編寫,因此又拾起了很久沒有寫的 Python,讓我無語的是總是遇到各種中文亂碼的問題,所以趁著周末,總結(jié)一下遇到的中文亂碼問題和對應(yīng)的解決方案,以及為什么會出現(xiàn)中文亂碼的問題。
你可能遇到下列的各種問題:
- 1、SyntaxError: Non-ASCII character '\xe4' in file XXX
- 2、UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position 0: ordinal not in range(128)
- 3、......
先聊聊字符編碼
- 當(dāng)我們處理文本時(shí),字符編碼是一個(gè)關(guān)鍵的概念。字符編碼是一種將字符映射到數(shù)字表示的方式。在計(jì)算機(jī)中,文本通常以數(shù)字的形式存儲和處理,而字符編碼就是定義這種映射關(guān)系的規(guī)則。
- 在選擇字符編碼時(shí),需要考慮存儲空間、兼容性和處理效率等因素。UTF-8 目前是使用最廣泛的字符編碼,因?yàn)樗诩嫒菪院凸?jié)省空間方面都有良好的表現(xiàn)。下面對常用的編碼做一個(gè)簡單的介紹:
ASCII(American Standard Code for Information Interchange)
ASCII 是一種最早的字符編碼,它使用 7 位二進(jìn)制數(shù)字(0 到 127)表示常用的字符,包括字母、數(shù)字、標(biāo)點(diǎn)符號和一些控制字符。由于只使用 7 位,ASCII 編碼總共可以表示 128 個(gè)字符。
Unicode
為了解決 ASCII 編碼的局限性,Unicode 應(yīng)運(yùn)而生。Unicode 是一個(gè)更為龐大的字符集,它包括世界上幾乎所有的字符,符號和標(biāo)點(diǎn)。每個(gè)字符都被分配一個(gè)唯一的數(shù)字,這個(gè)數(shù)字可能是 16 位(UCS-2)或者 32 位(UCS-4)。然而,Unicode 的缺點(diǎn)在于它需要更多的存儲空間。
UTF-8(Unicode Transformation Format - 8-bit)
為了解決 Unicode 存儲空間的問題,出現(xiàn)了 UTF-8 編碼。UTF-8 使用不定長度的編碼方案,能夠根據(jù)字符的不同使用 1 到 4 個(gè)字節(jié)表示一個(gè)字符。它保留了與 ASCII 兼容的部分,因此可以在現(xiàn)有的 ASCII 系統(tǒng)中無縫使用,并且在表示非常用字符時(shí)能夠更加節(jié)省空間。
UTF-16
UTF-16 是 Unicode 的一種實(shí)現(xiàn)方式,使用 16 位編碼方案,每個(gè)字符使用 2 個(gè)字節(jié)表示。UTF-16 的一個(gè)特點(diǎn)是在表示一些非常用字符時(shí)可能會使用額外的一個(gè)或兩個(gè)字節(jié)。
UTF-32
UTF-32 是 Unicode 的一種實(shí)現(xiàn)方式,使用 32 位編碼方案,每個(gè)字符使用 4 個(gè)字節(jié)表示。UTF-32 的特點(diǎn)是每個(gè)字符都使用相同的固定長度,方便在字符串中進(jìn)行隨機(jī)訪問。
分析中文亂碼、編碼問題的原因
從上面我們其實(shí)已經(jīng)大概可以知道我們中文亂碼、編碼問題的罪魁禍?zhǔn)祝壕幋a方式不匹配。下面我將對一些常見的案例進(jìn)行分析以及提供一些常見的解決方案。
常見案例
Python 版本 2.7
案例一
看一下下面兩個(gè)例子:.py 文件的編碼格式為UTF-8,Python2默認(rèn)使用ASCII解碼:
# 例一 s = "hello" print s // 正常執(zhí)行 hello # 例二 s = "你好" print s // 執(zhí)行失敗 SyntaxError: Non-ASCII character '\xe4' in file XXX
- 有些同學(xué)可能會比較疑惑,同樣是編碼格式不匹配,為什么例一可以正常執(zhí)行,而例二執(zhí)行失敗。
- 這是因?yàn)椋?code>UTF-8 是一種向后兼容的編碼方式。如果文件中只包含 ASCII 字符,那么該文件符合 UTF-8 編碼規(guī)范。這種特性使得使用 UTF-8 編碼時(shí),與使用 ASCII 編碼的現(xiàn)有系統(tǒng)可以很好地協(xié)同工作。
- 例一由于文件中只包含 ASCII 字符,即使使用 UTF-8 編碼,編碼結(jié)果和 ASCII 編碼一致,當(dāng)然也可以使用 ASCII 正常解碼。
解決方案
指定文件的編碼格式,讓執(zhí)行器使用指定的編碼方式解析文件。
# coding=utf-8 s = "你好" print s # 正常執(zhí)行 #你好
參考
PEP 263 – Defining Python Source Code Encodings
案例二
我們看一下下面的代碼:
# coding=utf-8
import xlwt
comment_list = [["標(biāo)題", "序號"], [1, 2]]
code = "1234"
# Create a new workbook and add a sheet
workbook = xlwt.Workbook()
sheet = workbook.add_sheet('Sheet1')
for row_index, row_data in enumerate(comment_list):
for col_index, cell_data in enumerate(row_data):
sheet.write(row_index, col_index, cell_data)
workbook.save('./%s.xls' % code)
#程序執(zhí)行報(bào)錯(cuò)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
'ascii' 無法解碼字節(jié) 0xe6 (UTF-8中的中文字符)
#如果寫入的數(shù)據(jù)中沒有中文數(shù)據(jù)是可以正常執(zhí)行的原因
#我們看下這個(gè)對象的構(gòu)造器:workbook = xlwt.Workbook() 可以看到默認(rèn)使用的是 ascii 編碼,而我們傳入的數(shù)據(jù)是 UTF-8 (包含中文字符),因此無法解析
#################################################################
## Constructor
#################################################################
def __init__(self, encoding='ascii', style_compression=0):
self.encoding = encoding
self.__owner = 'None'
self.__country_code = None # 0x07 is Russia :-)
self.__wnd_protect = 0
self.__obj_protect = 0
解決方案
從上面我們已經(jīng)知道是編碼不一致的原因,因此我們只需要將Workbook編碼調(diào)整為相同即可,比如調(diào)整為utf-8:
# coding=utf-8
import xlwt
comment_list = [["標(biāo)題", "序號"], [1, 2]]
code = "1234"
# Create a new workbook and add a sheet
workbook = xlwt.Workbook(encoding="utf-8")
sheet = workbook.add_sheet('Sheet1')
for row_index, row_data in enumerate(comment_list):
for col_index, cell_data in enumerate(row_data):
sheet.write(row_index, col_index, cell_data)
workbook.save('./%s.xls' % code)
這里如果你不想用 utf-8 編碼,你想用 gbk 編碼怎么實(shí)現(xiàn)呢?其實(shí)中心思想是相同的,只要保證編碼格式一致即可:
# coding=utf-8
import sys
import xlwt
comment_list = [["標(biāo)題", "續(xù)保"], [1, 2]]
print "default encoding:", sys.getdefaultencoding()
# Create a new workbook and add a sheet
workbook = xlwt.Workbook(encoding="gbk")
sheet = workbook.add_sheet('Sheet1')
for row_index, row_data in enumerate(comment_list):
for col_index, cell_data in enumerate(row_data):
sheet.write(row_index, col_index, str(cell_data).decode("utf-8"))
code = "1234"
workbook.save('./%s.xls' % code)
#程序執(zhí)行正常
default encoding: ascii
Python2 字符串的兩種表現(xiàn)形式
可能有朋友對上述案例中使用 gbk 方式中這段代碼 str(cell_data).decode("utf-8") 有點(diǎn)疑惑,這其實(shí)涉及到 Python2 字符串的兩種表現(xiàn)形式。
字節(jié)序列 和 Unicode 對象
在 Python 2 中,字符串類型被設(shè)計(jì)為字節(jié)序列而不是 Unicode 對象。這是 Python 2 與 Python 3 之間最重要的差異之一。在 Python 2 中,有兩種主要的字符串類型:
#1、str: 表示字節(jié)序列,是原始的字節(jié)串,而不涉及字符編碼。 #2、unicode: 表示 Unicode 字符串,用于處理字符編碼和文本 #示例: # 在 Python 2 中,默認(rèn)創(chuàng)建的是字節(jié)串而不是 Unicode 字符串 byte_str = "Hello, World!" print type(byte_str) # 輸出 <type 'str'> # 創(chuàng)建 Unicode 字符串 unicode_str = u"你好" print type(unicode_str) # 輸出 <type 'unicode'>
字節(jié)序列 和 Unicode 對象相互轉(zhuǎn)換
# 從 str 轉(zhuǎn)換成 unicode
print byte_str.decode('utf-8')
# 從 unicode 轉(zhuǎn)換成 str
print unicode_str.encode('utf-8')
有朋友也許很奇怪,為什么從 str 到 unicode 使用 decode,而 unicode 轉(zhuǎn)換成 str 使用 encode,其實(shí)這是因?yàn)?Python 認(rèn)為 16 位的 unicode 才是字符的唯一內(nèi)碼,而大家常用的字符集如 gb2312,gb18030/gbk,utf-8,以及 ascii 都是字符的二進(jìn)制(字節(jié))編碼形式。因此從 unicode 到其它二進(jìn)制編碼格式都使用 encode。
字符串運(yùn)算
在進(jìn)行同時(shí)包含 str 與 unicode 的運(yùn)算時(shí),Python 一律都把 str 轉(zhuǎn)換成 unicode 再運(yùn)算,當(dāng)然運(yùn)算結(jié)果也是 unicode。
str(cell_data).decode("utf-8") 寫法的原因
# 轉(zhuǎn)換為 str 類型
str(cell_data)
# 這里為什么么需要先 decode("utf-8") 轉(zhuǎn)為 unicode
# 實(shí)際上 Python 運(yùn)行時(shí)并不知道 str 的編碼,因此需要開發(fā)者指定正確的編碼方式進(jìn)行解碼
# 如果開發(fā)者不指定編碼方式進(jìn)行手動解碼則會使用 sys.getdefaultencoding() 配置的值 ascii 進(jìn)行解碼
str(cell_data).decode("utf-8")
# 由于我們在程序開頭指定了編碼方式為 utf-8 即 str 的編碼格式,如果這樣寫:
sheet.write(row_index, col_index, cell_data)
#程序執(zhí)行異常:
#UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
#因?yàn)?ascii 編碼無法解析帶有中文的 utf-8 編碼
案例三
案例三我們來聊聊,有關(guān)控制臺打印亂碼的問題。

上面的示例中,控制臺的編碼方式為 gbk,當(dāng)按照 utf-8 字符集打印時(shí),控制臺無法解析,出現(xiàn)亂碼。
實(shí)際上我們打印控制臺時(shí),實(shí)際上打印的是字符串的字符集編碼,但一般情況會根據(jù)系統(tǒng)的字符集設(shè)置來將字符編碼輸出到控制臺。如果出現(xiàn)打印亂碼,說明字符集匹配失敗或控制臺不支持該編碼格式,我們可以根據(jù)實(shí)際情況進(jìn)行確認(rèn)。
總結(jié)
本文從 Python2.7 中出現(xiàn)的一些亂碼案例出發(fā),從字符編碼、Python2.7字符原理分析了中文亂碼、編碼問題導(dǎo)致的原因并給出相應(yīng)的解決方案,實(shí)際上在編碼亂碼分析這一塊在其它語言上也是共通的,可以作為一定的參考依據(jù)。
在 Python 3 中,字符串默認(rèn)是 Unicode 類型,這樣可以更容易地處理文本數(shù)據(jù)。由于 Python 2 已于 2020 年停止維護(hù),建議在可能的情況下遷移到 Python 3,以便利用更現(xiàn)代和更直觀的字符串處理方式。
一些建議:
1、Python3 以前使用字符串,建議都帶上前綴u,在 Python 3 中,字符串默認(rèn)是 Unicode。Unicode 支持編碼自動轉(zhuǎn)換。
比如同樣是上文的案例,你不再需要解碼
# coding=utf-8
import sys
import xlwt
comment_list = [[u"標(biāo)題", u"續(xù)保"], [1, 2]]
print "default encoding:", sys.getdefaultencoding()
# Create a new workbook and add a sheet
workbook = xlwt.Workbook(encoding="gbk")
sheet = workbook.add_sheet('Sheet1')
for row_index, row_data in enumerate(comment_list):
for col_index, cell_data in enumerate(row_data):
sheet.write(row_index, col_index, cell_data)
code = "1234"
workbook.save('./%s.xls' % code)2、不要用str()函數(shù),使用unicode()代替。
3、不要用過時(shí)的 string 模塊(string 模塊已經(jīng)停止了更新,只保留了 ASCII 碼的支持,不再推薦使用,Python 保留該模塊僅僅是為了向后兼容)
4、非必要時(shí),不需要對 unicode 字符進(jìn)行編碼,因?yàn)榇蠖鄶?shù)情況會自動編碼解碼。
到此這篇關(guān)于一文教你解決所有Python中文亂碼問題的文章就介紹到這了,更多相關(guān)Python中文亂碼解決內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python使用內(nèi)置json模塊解析json格式數(shù)據(jù)的方法
這篇文章主要介紹了Python使用內(nèi)置json模塊解析json格式數(shù)據(jù)的方法,結(jié)合實(shí)例形式分析了Python使用內(nèi)置的json模塊實(shí)現(xiàn)json格式數(shù)據(jù)的解析、轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2017-07-07
Python中動態(tài)創(chuàng)建類實(shí)例的方法
在Java中我們可以通過反射來根據(jù)類名創(chuàng)建類實(shí)例,那么在Python我們怎么實(shí)現(xiàn)類似功能呢?其實(shí)在Python有一個(gè)builtin函數(shù)import,我們可以使用這個(gè)函數(shù)來在運(yùn)行時(shí)動態(tài)加載一些模塊2017-03-03
Python中時(shí)間datetime的處理與轉(zhuǎn)換用法總結(jié)
今天小編就為大家分享一篇關(guān)于Python中時(shí)間datetime的處理與轉(zhuǎn)換用法總結(jié),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-02-02
對python操作kafka寫入json數(shù)據(jù)的簡單demo分享
今天小編就為大家分享一篇對python操作kafka寫入json數(shù)據(jù)的簡單demo,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12
Python實(shí)現(xiàn)的txt文件去重功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)的txt文件去重功能,涉及Python針對txt文本文件的讀寫、字符串遍歷、判斷相關(guān)操作技巧,需要的朋友可以參考下2018-07-07
python 簡單搭建阻塞式單進(jìn)程,多進(jìn)程,多線程服務(wù)的實(shí)例
下面小編就為大家?guī)硪黄猵ython 簡單搭建阻塞式單進(jìn)程,多進(jìn)程,多線程服務(wù)的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
python實(shí)現(xiàn)socket簡單通信的示例代碼
這篇文章主要介紹了python實(shí)現(xiàn)socket簡單通信的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
如何分離django中的媒體、靜態(tài)文件和網(wǎng)頁
這篇文章主要介紹了如何分離django中的媒體、靜態(tài)文件和網(wǎng)頁,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Django異步任務(wù)線程池實(shí)現(xiàn)原理
這篇文章主要介紹了Django異步任務(wù)線程池實(shí)現(xiàn)原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12

