一文教你解決所有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、......
先聊聊字符編碼
- 當我們處理文本時,字符編碼是一個關(guān)鍵的概念。字符編碼是一種將字符映射到數(shù)字表示的方式。在計算機中,文本通常以數(shù)字的形式存儲和處理,而字符編碼就是定義這種映射關(guān)系的規(guī)則。
- 在選擇字符編碼時,需要考慮存儲空間、兼容性和處理效率等因素。UTF-8 目前是使用最廣泛的字符編碼,因為它在兼容性和節(jié)省空間方面都有良好的表現(xiàn)。下面對常用的編碼做一個簡單的介紹:
ASCII(American Standard Code for Information Interchange)
ASCII 是一種最早的字符編碼,它使用 7 位二進制數(shù)字(0 到 127)表示常用的字符,包括字母、數(shù)字、標點符號和一些控制字符。由于只使用 7 位,ASCII 編碼總共可以表示 128 個字符。
Unicode
為了解決 ASCII 編碼的局限性,Unicode 應(yīng)運而生。Unicode 是一個更為龐大的字符集,它包括世界上幾乎所有的字符,符號和標點。每個字符都被分配一個唯一的數(shù)字,這個數(shù)字可能是 16 位(UCS-2)或者 32 位(UCS-4)。然而,Unicode 的缺點在于它需要更多的存儲空間。
UTF-8(Unicode Transformation Format - 8-bit)
為了解決 Unicode 存儲空間的問題,出現(xiàn)了 UTF-8 編碼。UTF-8 使用不定長度的編碼方案,能夠根據(jù)字符的不同使用 1 到 4 個字節(jié)表示一個字符。它保留了與 ASCII 兼容的部分,因此可以在現(xiàn)有的 ASCII 系統(tǒng)中無縫使用,并且在表示非常用字符時能夠更加節(jié)省空間。
UTF-16
UTF-16 是 Unicode 的一種實現(xiàn)方式,使用 16 位編碼方案,每個字符使用 2 個字節(jié)表示。UTF-16 的一個特點是在表示一些非常用字符時可能會使用額外的一個或兩個字節(jié)。
UTF-32
UTF-32 是 Unicode 的一種實現(xiàn)方式,使用 32 位編碼方案,每個字符使用 4 個字節(jié)表示。UTF-32 的特點是每個字符都使用相同的固定長度,方便在字符串中進行隨機訪問。
分析中文亂碼、編碼問題的原因
從上面我們其實已經(jīng)大概可以知道我們中文亂碼、編碼問題的罪魁禍首:編碼方式不匹配。下面我將對一些常見的案例進行分析以及提供一些常見的解決方案。
常見案例
Python
版本 2.7
案例一
看一下下面兩個例子:.py
文件的編碼格式為UTF-8,Python2
默認使用ASCII解碼:
# 例一 s = "hello" print s // 正常執(zhí)行 hello # 例二 s = "你好" print s // 執(zhí)行失敗 SyntaxError: Non-ASCII character '\xe4' in file XXX
- 有些同學(xué)可能會比較疑惑,同樣是編碼格式不匹配,為什么例一可以正常執(zhí)行,而例二執(zhí)行失敗。
- 這是因為:
UTF-8 是一種向后兼容的編碼方式。如果文件中只包含 ASCII 字符,那么該文件符合 UTF-8 編碼規(guī)范。這種特性使得使用 UTF-8 編碼時,與使用 ASCII 編碼的現(xiàn)有系統(tǒng)可以很好地協(xié)同工作。
- 例一由于文件中只包含 ASCII 字符,即使使用 UTF-8 編碼,編碼結(jié)果和 ASCII 編碼一致,當然也可以使用 ASCII 正常解碼。
解決方案
指定文件的編碼格式,讓執(zhí)行器使用指定的編碼方式解析文件。
# coding=utf-8 s = "你好" print s # 正常執(zhí)行 #你好
參考
PEP 263 – Defining Python Source Code Encodings
案例二
我們看一下下面的代碼:
# coding=utf-8 import xlwt comment_list = [["標題", "序號"], [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í)行報錯 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òu)造器:workbook = xlwt.Workbook() 可以看到默認使用的是 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 = [["標題", "序號"], [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
編碼怎么實現(xiàn)呢?其實中心思想是相同的,只要保證編碼格式一致即可:
# coding=utf-8 import sys import xlwt comment_list = [["標題", "續(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")
有點疑惑,這其實涉及到 Python2
字符串的兩種表現(xiàn)形式。
字節(jié)序列 和 Unicode 對象
在 Python 2 中,字符串類型被設(shè)計為字節(jié)序列而不是 Unicode 對象。這是 Python 2 與 Python 3 之間最重要的差異之一。在 Python 2 中,有兩種主要的字符串類型:
#1、str: 表示字節(jié)序列,是原始的字節(jié)串,而不涉及字符編碼。 #2、unicode: 表示 Unicode 字符串,用于處理字符編碼和文本 #示例: # 在 Python 2 中,默認創(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
,其實這是因為 Python
認為 16 位的 unicode
才是字符的唯一內(nèi)碼,而大家常用的字符集如 gb2312
,gb18030/gbk
,utf-8
,以及 ascii
都是字符的二進制(字節(jié))編碼形式。因此從 unicode
到其它二進制編碼格式都使用 encode
。
字符串運算
在進行同時包含 str 與 unicode 的運算時,Python 一律都把 str 轉(zhuǎn)換成 unicode 再運算,當然運算結(jié)果也是 unicode。
str(cell_data).decode("utf-8") 寫法的原因
# 轉(zhuǎn)換為 str 類型 str(cell_data) # 這里為什么么需要先 decode("utf-8") 轉(zhuǎn)為 unicode # 實際上 Python 運行時并不知道 str 的編碼,因此需要開發(fā)者指定正確的編碼方式進行解碼 # 如果開發(fā)者不指定編碼方式進行手動解碼則會使用 sys.getdefaultencoding() 配置的值 ascii 進行解碼 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) #因為 ascii 編碼無法解析帶有中文的 utf-8 編碼
案例三
案例三我們來聊聊,有關(guān)控制臺打印亂碼的問題。
上面的示例中,控制臺的編碼方式為 gbk
,當按照 utf-8
字符集打印時,控制臺無法解析,出現(xiàn)亂碼。
實際上我們打印控制臺時,實際上打印的是字符串的字符集編碼,但一般情況會根據(jù)系統(tǒng)的字符集設(shè)置來將字符編碼輸出到控制臺。如果出現(xiàn)打印亂碼,說明字符集匹配失敗或控制臺不支持該編碼格式,我們可以根據(jù)實際情況進行確認。
總結(jié)
本文從 Python2.7
中出現(xiàn)的一些亂碼案例出發(fā),從字符編碼、Python2.7
字符原理分析了中文亂碼、編碼問題導(dǎo)致的原因并給出相應(yīng)的解決方案,實際上在編碼亂碼分析這一塊在其它語言上也是共通的,可以作為一定的參考依據(jù)。
在 Python 3 中,字符串默認是 Unicode 類型,這樣可以更容易地處理文本數(shù)據(jù)。由于 Python 2 已于 2020 年停止維護,建議在可能的情況下遷移到 Python 3,以便利用更現(xiàn)代和更直觀的字符串處理方式。
一些建議:
1、Python3 以前使用字符串,建議都帶上前綴u,在 Python 3 中,字符串默認是 Unicode。Unicode 支持編碼自動轉(zhuǎn)換。
比如同樣是上文的案例,你不再需要解碼
# coding=utf-8 import sys import xlwt comment_list = [[u"標題", 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、不要用過時的 string 模塊(string 模塊已經(jīng)停止了更新,只保留了 ASCII 碼的支持,不再推薦使用,Python 保留該模塊僅僅是為了向后兼容)
4、非必要時,不需要對 unicode 字符進行編碼,因為大多數(shù)情況會自動編碼解碼。
到此這篇關(guān)于一文教你解決所有Python中文亂碼問題的文章就介紹到這了,更多相關(guān)Python中文亂碼解決內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python人工智能human?learn繪圖創(chuàng)建機器學(xué)習(xí)模型
這篇文章主要為大家介紹了python人工智能human?learn繪圖就可以創(chuàng)建機器學(xué)習(xí)模型的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-11-11Python編程使用Selenium模擬淘寶登錄實現(xiàn)過程
這篇文章主要介紹了Python編程使用Selenium模擬淘寶登錄的實現(xiàn)過程示例及解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2021-10-10