Python3報錯處理:UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)


一、背景說明

最開始不願意使用Python,一大原因是因為Python2默認使用ASCII編碼處理中文可以說是一件痛苦的事情。僅從更換默認編碼一項變換,就可以說Python3和Python2不算同一門語言。

Python3更換為默認使用Unicode(utf-8)編碼,一直使用下來再沒有遇到編碼問題帶來的困撓,似乎編碼問題在Python3時代就該完全消失的。但這兩天遇到了一個問題。

在調用一個庫時,出現了一個異常報錯類似如UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128),幾經排查之下發現只要該庫返回結果包含中文,我這邊使用print()打印該結果時就會出現該異常。

 

二、原因分析

2.1 數據要經過編碼才能傳輸

我們知道數據在網絡上傳輸時,需要先編碼;平時我們可能並不注意,但現在要明確,編碼的原因不在於網絡而在於傳輸。

print()相當於把字符串從內存傳輸到了tty上,所以print()是需要encode()動作的;平時我們print()時一般都不需要encode(),只是因為當print()檢測到傳來的參數是不是byte類型時自動進行了編碼。

 

2.2 print()使用何種編碼

Python3默認使用的是utf-8,這可以通過sys.getdefaultencoding()進行確認。但這只是默認,當系統配置了LC_ALL、LC_CTYPE、LANG等環境變量時(三者優先級從高到低),Python3采用這些變量配置的編碼;如果這些變量配置的是utf-8那Python3用的就還是utf-8,但如果不是utf-8那Python3所用的也就不是utf-8了。

當前使用的編碼可通過sys.getfilesystemencoding()獲取。

 

三、場景復現

為簡單起見,我們這里直接以打印一個中文字符串作為演示,示例代碼如下(我這里保存成test_encode.py):

import sys

class TestEncode():
    def __int__(self):
        pass

    def main_logic(self):
        # 打印語言默認編碼
        print(f"defaultencoding--{sys.getdefaultencoding()}")
        # 打印系統配置的編碼
        print(f"filesystemencoding--{sys.getfilesystemencoding()}")

        # 最后嘗試打印中文
        print("中文")

if __name__ == "__main__":
    obj = TestEncode()
    obj.main_logic()

shell依次執行如下命令:

# 查看當前編碼情況
locale
# 確認在utf-8情況下打印中文無誤
python3 test_encode.py

# 設置LC_TYPE,C代表ASCII
export LC_CTYPE="C"
# 查看當前編碼情況
locale
# 再次運行,確認系統編碼已改變,並出現編碼錯誤
python3 test_encode.py

最終結果如下,在系統編碼配置為utf-8時打印正常,在系統編碼改為C(即ASCII)后打印報編碼異常(不過我在root用戶環境修改編碼一直不成功,不懂我電腦有點問題還是什么原因):

 

四、解決辦法

其實追根究底,打印報錯本質原因就是標准輸出的編碼不支持要打印的字符,對中文而言就是不是utf8(當然要說的話還可以是gbk這些),那解決辦法就是去把標准輸出的編碼給設成utf8就完事了。

那各語言的標准輸出編碼由什么決定呢,一般是語言底層根據一些自己的變量去設置標准輸出(python是PYTHONENCODING等幾個),如果沒有那就取系統的LC_ALL等配置編碼的值去設置標准輸出。

那基於這個事實,我們就有了以下三種解決辦法。

方法一:自己臨時自行修改標准輸出為utf-8

這種方法最保險,能確認自己不被覆蓋;但如果是一個項目那得確保在一開始設置,不然每次print前設置一次也很麻煩。

import sys
import codecs sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) print("中文")

方法二:設置語言會采用的的環境變量PYTHONIOENCODING

這種方法按理感覺有時有bug,該環境變量在一些解析器中沒被采用。

PYTHONIOENCODING=utf-8 python test_encode.py

方法三:設置語言會采用的系統環境變量值為utf-8

這種方法各種語言應該都通用。

如果配了變量仍有報錯,那一般都是運行的代碼沒正確讀取變量所致,可以在腳本中獲取下這變量值確認,python是os.environ。

# 優先級 LC_ALL > LC_*(包括決定月分顯示語言的LC_TIME等)> LANG
export LC_ALL="en_US.utf8"

 

參考:

https://blog.csdn.net/th_num/article/details/80685389

https://timothyqiu.com/archives/surrogateescape-in-python-3/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM