一文教你解決所有Python中文亂碼問題
前言
最近偶爾會幫忙寫一些爬蟲代碼,有一些需要使用 Python
編寫,因此又拾起了很久沒有寫的 Python
,讓我無語的是總是遇到各種中文亂碼的問題,所以趁著周末,總結一下遇到的中文亂碼問題和對應的解決方案,以及為什么會出現(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、......
先聊聊字符編碼
- 當我們處理文本時,字符編碼是一個關鍵的概念。字符編碼是一種將字符映射到數(shù)字表示的方式。在計算機中,文本通常以數(shù)字的形式存儲和處理,而字符編碼就是定義這種映射關系的規(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 應運而生。Unicode 是一個更為龐大的字符集,它包括世界上幾乎所有的字符,符號和標點。每個字符都被分配一個唯一的數(shù)字,這個數(shù)字可能是 16 位(UCS-2)或者 32 位(UCS-4)。然而,Unicode 的缺點在于它需要更多的存儲空間。
UTF-8(Unicode Transformation Format - 8-bit)
為了解決 Unicode 存儲空間的問題,出現(xiàn)了 UTF-8 編碼。UTF-8 使用不定長度的編碼方案,能夠根據字符的不同使用 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 的特點是每個字符都使用相同的固定長度,方便在字符串中進行隨機訪問。
分析中文亂碼、編碼問題的原因
從上面我們其實已經大概可以知道我們中文亂碼、編碼問題的罪魁禍首:編碼方式不匹配。下面我將對一些常見的案例進行分析以及提供一些常見的解決方案。
常見案例
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
- 有些同學可能會比較疑惑,同樣是編碼格式不匹配,為什么例一可以正常執(zhí)行,而例二執(zhí)行失敗。
- 這是因為:
UTF-8 是一種向后兼容的編碼方式。如果文件中只包含 ASCII 字符,那么該文件符合 UTF-8 編碼規(guī)范。這種特性使得使用 UTF-8 編碼時,與使用 ASCII 編碼的現(xiàn)有系統(tǒng)可以很好地協(xié)同工作。
- 例一由于文件中只包含 ASCII 字符,即使使用 UTF-8 編碼,編碼結果和 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ù)據中沒有中文數(shù)據是可以正常執(zhí)行的
原因
#我們看下這個對象的構造器:workbook = xlwt.Workbook() 可以看到默認使用的是 ascii 編碼,而我們傳入的數(shù)據是 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
解決方案
從上面我們已經知道是編碼不一致的原因,因此我們只需要將Workbook
編碼調整為相同即可,比如調整為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 中,字符串類型被設計為字節(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 對象相互轉換
# 從 str 轉換成 unicode print byte_str.decode('utf-8') # 從 unicode 轉換成 str print unicode_str.encode('utf-8')
有朋友也許很奇怪,為什么從 str
到 unicode
使用 decode
,而 unicode
轉換成 str
使用 encode
,其實這是因為 Python
認為 16 位的 unicode
才是字符的唯一內碼,而大家常用的字符集如 gb2312
,gb18030/gbk
,utf-8
,以及 ascii
都是字符的二進制(字節(jié))編碼形式。因此從 unicode
到其它二進制編碼格式都使用 encode
。
字符串運算
在進行同時包含 str 與 unicode 的運算時,Python 一律都把 str 轉換成 unicode 再運算,當然運算結果也是 unicode。
str(cell_data).decode("utf-8") 寫法的原因
# 轉換為 str 類型 str(cell_data) # 這里為什么么需要先 decode("utf-8") 轉為 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 編碼
案例三
案例三我們來聊聊,有關控制臺打印亂碼的問題。
上面的示例中,控制臺的編碼方式為 gbk
,當按照 utf-8
字符集打印時,控制臺無法解析,出現(xiàn)亂碼。
實際上我們打印控制臺時,實際上打印的是字符串的字符集編碼,但一般情況會根據系統(tǒng)的字符集設置來將字符編碼輸出到控制臺。如果出現(xiàn)打印亂碼,說明字符集匹配失敗或控制臺不支持該編碼格式,我們可以根據實際情況進行確認。
總結
本文從 Python2.7
中出現(xiàn)的一些亂碼案例出發(fā),從字符編碼、Python2.7
字符原理分析了中文亂碼、編碼問題導致的原因并給出相應的解決方案,實際上在編碼亂碼分析這一塊在其它語言上也是共通的,可以作為一定的參考依據。
在 Python 3 中,字符串默認是 Unicode 類型,這樣可以更容易地處理文本數(shù)據。由于 Python 2 已于 2020 年停止維護,建議在可能的情況下遷移到 Python 3,以便利用更現(xiàn)代和更直觀的字符串處理方式。
一些建議:
1、Python3 以前使用字符串,建議都帶上前綴u,在 Python 3 中,字符串默認是 Unicode。Unicode 支持編碼自動轉換。
比如同樣是上文的案例,你不再需要解碼
# 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 模塊已經停止了更新,只保留了 ASCII 碼的支持,不再推薦使用,Python 保留該模塊僅僅是為了向后兼容)
4、非必要時,不需要對 unicode 字符進行編碼,因為大多數(shù)情況會自動編碼解碼。
到此這篇關于一文教你解決所有Python中文亂碼問題的文章就介紹到這了,更多相關Python中文亂碼解決內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Python開發(fā)WebService系列教程之REST,web.py,eurasia,Django
對于今天的WebService開發(fā),我們至少有兩種選擇:SOAP/WSDL/UDDI系列的; REST風格架構系列的 ?。?!2014-06-06基于Python編寫一個有趣的進程勾選器(Process?Selector)
本文主要介紹了如何利用Python編寫一個有趣的進程勾選器,可以在Checklistbox中列出系統(tǒng)中正在運行的進程的名稱和PID,并允許用戶選擇進程并將其保存到文本文件中,需要的可以參考一下2023-05-05詳解Python 2.6 升級至 Python 2.7 的實踐心得
本篇文章主要介紹了詳解Python 2.6 升級至 Python 2.7 的實踐心得,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04Python學習之魔法函數(shù)(filter,map,reduce)詳解
這篇文章我們將來學習一下,Python中的三個高級函數(shù):filter()、map()、reduce(),這三個函數(shù)也被稱為魔法函數(shù),感興趣的小伙伴可以了解一下2022-04-04