日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python3 rid1.7.4.2 控制台中文乱码_TL;DR - 有关 Python 2 和 Sublime Text 中文 Unicode 编码问题的分析与理解...

發布時間:2024/7/19 python 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python3 rid1.7.4.2 控制台中文乱码_TL;DR - 有关 Python 2 和 Sublime Text 中文 Unicode 编码问题的分析与理解... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

TL;DR

問題背景:

相信很多用 Sublime Text 來寫 Python 2 的同學都遇到過以下這個問題(例如這位同學 /t/100435 和這位同學/t/163012 ):

在 Sublime Text 里用 Cmd (Ctrl) + B 運行代碼 print u'中文',想要打印出 unicode 類型的字符串時,會出現以下報錯:

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

傳說中的 Python 2 編碼坑(笑)

而同樣的 print u'中文' 代碼在 Mac 的終端里卻能正常打印出 “中文” 結果,沒有任何報錯。

雖然在網上能查到多種解決方法,但一直以來知其然而不知其所以然,不了解為什么那些方法能解決問題的真正原因,也不知道為什么同樣的代碼在終端里就可以運行而在 Sublime Text 里就不行了?

因此我研究學習了下這個問題相關的一些 Python 2 編碼問題,在這里分享下我的理解。

以下屬于新手向,參考了網上多篇文章,如有錯誤,望指正。

先說下我的環境:

Mac OS X

Python 2.7

Sublime Text 3

分析:

Python 在向控制臺 (console) print 的時候,因為控制臺只能看得懂由 bytes(字節序列)組成的字符串,而 Python 中 "unicode" 對象存儲的是 code points(碼點),因此 Python 需要將輸出中的 "unicode" 對象用編碼轉換為儲存 bytes(字節序列)的 "str" 對象后,才能進行輸出。

而在報錯里看到 UnicodeEncodeError, 那就說明 Python 在將 unicode 轉換為 str 時使用了錯誤的編碼。而為什么是 'ascii' 編碼呢?那是因為 Python 2 的默認編碼就是 ASCII,可以通過以下命令來查看 Python 的默認編碼:

>>> import sys

>>> print sys.getdefaultencoding()

ascii

所以此時在 Sublime Text 里運行 print u'中文',實際上等于是運行了:

print u'中文'.encode('ascii')

ASCII 編碼無法對 unicode 的中文進行編碼,因此就報錯了。

那為什么同樣的代碼 print u'中文' 在 Mac 的終端里卻能正常輸出中文,難道是因為終端下的 Python 2 的默認編碼不是 ASCII?非也,在終端下運行 sys.getdefaultencoding() 結果一樣是 ascii。那同樣是 ascii 為什么會有不同的結果?難倒這里 Python 用了另外一個編碼來轉換?

是的,其實 Python 在 print unicode 時真正涉及到的是另一組編碼:stdin/stdout/stderr 的編碼,也就是標準輸入、標準輸出和標準錯誤輸出的編碼。可以通過以下命令來查看,這里是在我的終端下運行的結果:

>>> import sys

>>> print sys.stdin.encoding

UTF-8

>>> print sys.stdout.encoding

UTF-8

>>> print sys.stderr.encoding

UTF-8

在正常情況下,Python 2 在 print unicode 時用來轉換的編碼并不是 Python 的默認編碼 sys.getdefaultencoding(),而是 sys.stdout.encoding 所設的編碼。

因為在我的終端下 Python 的 sys.stdout.encoding 編碼是 UTF-8,所以在終端里運行 print u'中文' 時,實際上是等于運行了:

print u'中文'.encode('UTF-8')

編碼正確,運行正常,因此沒有報錯。

在類 UNIX 系統下,Python 應該是通過環境變量 LC_CTYPE 來判斷 stdin/stdout/stderr 的編碼的。因此一般只要將 shell 的 LANG 環境變量設置對為 **_**.UTF-8 后,應該就能在終端里直接 print 出 unicode 類型的字符串了,而不需要在 print 時手動加上 .encode('utf-8') 進行編碼了。

但在 Sublime Text 里事情就沒那么美好了。在 Sublime Text 里運行查看 stdout 編碼的命令,發現:

import sys

print sys.stdout.encoding

-----------------------------"""

None

[Finished in 0.1s]

結果甚至不是 'ascii' 而是 None。可能是因為 Sublime Text 的 Build System 是用 subprocess.Popen 來運行 Python 的,導致 Python 無法判斷出正確的 stdin/stdout/stderr 編碼,于是都變成 None 了。

這種情況也發生在輸出的目標是管道的情況下:

$python -c 'import sys; print sys.stdout.encoding' | tee /tmp/foo.txt

None

那么在這種 sys.stdout.encoding 為 None 情況下的 print unicode 怎么辦呢?答案就是 Python 只能很無奈地使用 sys.getdefaultencoding() 的默認編碼 ascii 來對 unicode 進行轉換了。這樣就出現了本文開頭所說的那個 UnicodeEncodeError 問題。

總結一下 Python 2 向控制臺 print 輸出時的流程:

Python 啟動時,當它發現當前的輸出是連接到控制臺的時候,它會根據一些環境變量,例如環境變量 LC_CTYPE,來設法判斷出 sys.stdin/stdout/stderr.encoding 編碼值。

當 Python 無法判斷出所需的編碼時,它會將 sys.stdin/stdout/stderr.encoding 的值設置為 None。

print 時判斷字符串是否是 unicode 類型。

如果是的話,并且 sys.stdout.encoding 不為 None 時,就使用 sys.stdout.encoding 編碼對 unicode 編碼成 str 后輸出。

如果 sys.stdout.encoding 為 None 的話,就使用 sys.getdefaultencoding() 默認編碼來對 unicode 進行轉換成 str 后輸出。

if sys.stdout.encoding:

print unicode.encode(sys.stdout.encoding)

else:

print unicode.encode(sys.getdefaultencoding())

解決方法:

解決方法 1:

先說最不正確的解決方法:在文件頭部加上

import sys

reload(sys)

sys.setdefaultencoding('utf-8')

這種方法通過 dirty hack 的方式在 Python 剛啟動時更改了 Python 的默認編碼為 utf-8。此后:

>>> print sys.getdefaultencoding()

utf-8

但就本文所討論的問題來說,這個方法并不是真正地直接解決了問題。就如上述所說,Python 只是在 sys.stdout.encoding 為 None 時才會使用默認編碼來轉換需要 print 的 unicode 字符串。那萬一在 sys.stdout.encoding 存在,但為 ascii 的情況下呢?這樣即使更改了 Python 的默認編碼,同樣還是會出現 UnicodeEncodeError 報錯。所以對本問題來說,這個方法治標不治本。

除此之外,很多人都用這個方法來解決 Python 2 下遇到的其它各種各樣的編碼問題,在 v2ex 的各種 Python 編碼問題討論帖中也常常能見到有人推薦用這個方法來解決問題的。

但實際上很多大牛都不推薦用這個方法來解決 Python 2 的編碼問題,這里引用下 StackOverflow 相關回答 里的一句話:

the use of sys.setdefaultencoding() has always been discouraged

為什么這個方法不被推薦呢?我們來看下 Python 文檔里對這個 function 是怎么說的:

This function is only intended to be used by the site module implementation and, where needed, by sitecustomize. Once used by the site module, it is removed from the sys module’s namespace.

可以看到這個方法原本就不是用戶向的方法,并沒有打算讓用戶用這個方法來更改 Python 2 的默認編碼。

那為什么不建議我們更改 Python 的默認編碼呢?

這里引用 Python 核心開發者、Python Unicode 支持的設計者和實現者: Marc-André Lemburg,他在一個郵件列表上的回復:

The only supported default encodings in Python are:

Python 2.x: ASCII

Python 3.x: UTF-8

If you change these, you are on your own and strange things will

start to happen. The default encoding does not only affect

the translation between Python and the outside world, but also

all internal conversions between 8-bit strings and Unicode.

Hacks like what's happening in the pango module (setting the

default encoding to 'utf-8' by reloading the site module in

order to get the sys.setdefaultencoding() API back) are just

downright wrong and will cause serious problems since Unicode

objects cache their default encoded representation.

Please don't enable the use of a locale based default encoding.

If all you want to achieve is getting the encodings of

stdout and stdin correctly setup for pipes, you should

instead change the .encoding attribute of those (only).

--

Marc-Andre Lemburg

eGenix.com

從此可見,Python 2 唯一支持的內部編碼只有 ASCII,更改其默認編碼為其它編碼可能會導致各種各樣奇怪的問題。在這里他也說了使用 sys.setdefaultencoding() 的方法是徹徹底底的錯誤,正確的方法應該是更改 stdout 和 stdin 的編碼。

所以這個方法是最不正確的填坑方法,請大家慎用。

解決方法 2:

然后說說應當是姿勢最正確的、也是大家都懂的方法:

在 print 的時候顯式地用正確的編碼來對 unicode 類型的字符串進行 encode('正確的編碼') 為 str 后, 再進行輸出。

而在 print 的時候,這個正確的編碼一般就是 sys.stdout.encoding 的值。但也正如上述所說,這個值并不是一直是可靠的,因此需要根據所使用的平臺和控制臺環境來判斷出這個正確的編碼。

而在 Mac 下這個正確的編碼一般都是 utf-8,因此若不考慮跨環境的話,可以無腦地一直用 encode('utf-8') 和 decode('utf-8') 來進行輸入輸出轉換。

在我的經驗中,這個策略也是解決 Python 2 其它 unicode 相關編碼問題的最佳方法。在 PyCon 2012 的一個演講中(關于 Python Unicode 問題很好的一個演講,這里有演講稿的中文翻譯版),對這個方法有一個很形象的比喻:

因為在程序中進進出出的只有存儲 bytes(字節序列)的 str。因此最好的策略是將輸入的 bytes 馬上解碼成 unicode,而在程序內部中均使用 unicode,而當在進行輸出的時候,盡早將之編碼成 bytes。

也就是要形成一個 Unicode 三明治(如圖), bytes 在外, Unicode 在內。在邊界的地方盡早進行 decode 和 encode。不要在內部混用 str 和 unicode,盡可能地讓程序處理的字符串都為 Unicode。

解決方法 3:

雖然解決方法 2 是最正確的方式,但是有時候在 Sublime Text 里調試些小腳本,實在是懶得再在每個 print 語句后面寫一個尾巴 .encode('utf-8')。那么有沒有辦法能讓 Sublime Text 像在終端里一樣直接就能 print u'中文' 呢?也就是說能不能解決 sys.stdin/stdout/stderr.encoding 為 None 的情況呢?

答案肯定是有的,一種方法是用類似更改默認編碼的方法一樣,用 dirty hack 的方式在 Python 代碼中去顯式地更改 sys.stdin/stdout/stderr.encoding 的值。一樣是不推薦,我也沒嘗試過,在這里就不詳說了。

另一種方法則是通過設置 PYTHONIOENCODING 環境變量來強制要求 Python 設置 stdin/stdout/stderr 的編碼值為我們想要的,這是一個相對比較干凈的解決方法。見文檔:

PYTHONIOENCODING

Overrides the encoding used for stdin/stdout/stderr, in the syntax encodingname:errorhandler. The :errorhandler part is optional and has the same meaning as in str.encode().

New in version 2.6.

在 Mac 下對全局 GUI 程序設置環境變量的方法是:使用 launchctl setenv <, ...> 命令對所有 launchd 啟動的未來子進程設置環境變量。

在這里順便科普下,為什么對所有 launchd 啟動的未來子進程設置環境變量可以使得對 Mac 下所有 GUI 程序生效。這是因為 launchd 是 OS X 系統啟動后運行的第一個非內核進程。我們可以在 activity monitor(活動監視器)里看到,它的 pid 是很帥氣的 1。而之后所有的進程都將是它的子進程。

另外還可以通過 launchd 在 Mac 下實現類 crontab 的功能。

launchctl setenv 命令設置的全局環境變量會在電腦重啟后失效,因此就需要通過上面說的 launchd 的開機啟動任務的功能來在重啟后再設置一遍環境變量,其配置方法可以參考這里。也因為這個原因,我并沒有使用這個方法來設置 PYTHONIOENCODING 環境變量。

而 Sublime Text 提供了一個設置 Build System 環境變量的方法,這個方法各平臺的 Sublime Text 都適用。

設置 Sublime Text 的 Python Build System 環境變量的步驟如下:

將 Sublime Text 默認的 Python Build System 的配置文件 Python.sublime-build(找到這個文件的最好方法是安裝插件 PackageResourceViewer)復制一份到 Sublime Text 的 /Packages/User 文件夾下(在 Mac 和 Sublime Text 3 下這個路徑是 ~/Library/Application Support/Sublime Text 3/Packages/User)。

打開編輯新復制來的 Python.sublime-build 文件,如下加上一行設置 PYTHONIOENCODING 環境變量為 UTF-8 編碼的內容,并保存:

{

"shell_cmd": "python -u \"$file\"",

"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",

+ "env": {"PYTHONIOENCODING": "utf8"},

"selector": "source.python"

}

這樣一來終于在這么長的文章后能在 Sublime Text 里直接運行 print u'中文',而不用再出現萬惡的 UnicodeEncodeError 了。

既然都研究到這了,不妨我們試試把 PYTHONIOENCODING 設置成其它編碼看看會出現什么情況,例如設置成簡體中文 Windows 的默認編碼 cp936:"env": {"PYTHONIOENCODING": "cp936"}

import sys

print sys.stdout.encoding

print u'你好'

----------------------------------"""

cp936

[Decode error - output not utf-8]

[Finished in 0.1s]

[Decode error - output not utf-8],這就是 Sublime Text 在 Windows 下可能會出現的問題(例如這兩位同學 /t/45391 /t/88428 )。這是因為 Sublime Text 的 Build System 默認是用 utf-8 編碼去解讀運行的輸出的,而我們指定了讓 Python 用 cp936 編碼來生成 str 字符串進行輸出,那么就會出現 Sublime Text 無法識別輸出的情況了。

同樣在對終端 export PYTHONIOENCODING=cp936 后,在終端下 print u'你好' 輸出的就會是 ��� 這樣的亂碼。

解決辦法之一就是同樣在 Python.sublime-build 文件里設置 "env": {"PYTHONIOENCODING": "utf8"} 來使得輸出統一為 utf-8。

或者是更改 Sublime Text 的 Build System 所接受的輸出編碼,將其改為一致的 cp936 編碼,同樣也是更改 Python.sublime-build 文件,加入一行:

{

"shell_cmd": "python -u \"$file\"",

"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",

+ "encoding": "cp936",

"selector": "source.python"

}

那我們再試試把這兩個設置同時都加到 Python.sublime-build 文件里,也就是讓 Python 輸出 utf8 編碼的字符串,而讓 Sublime Text 用 cp936 編碼來解讀,看看會發生什么情況?

{

"shell_cmd": "python -u \"$file\"",

"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",

+ "env": {"PYTHONIOENCODING": "utf8"},

+ "encoding": "cp936",

"selector": "source.python"

}

print u'你好'

----------------------"""

浣犲ソ

[Finished in 0.1s]

笑,居然不是 [Decode error - output not cp936],而是這么喜感的 “浣犲ソ”!

這是因為 “你好” 的 utf-8 編碼剛好和 “浣犲ソ” 的 cp936 編碼重合了,都是 '\xe6\xb5\xa3\xe7\x8a\xb2\xe3\x82\xbd',所以使用 cp936 編碼去解讀的 Sublime Text 就認為這段字符串就是 “浣犲ソ” 而顯示了出來。

>>> print repr('浣犲ソ') # cp936 編碼

'\xe6\xb5\xa3\xe7\x8a\xb2\xe3\x82\xbd'

>>> print repr(u'你好'.encode('utf-8')) # utf-8 編碼

'\xe6\xb5\xa3\xe7\x8a\xb2\xe3\x82\xbd'

附帶解決的問題:IDLE 的交互模式里無法輸入中文

我偶爾會用 Python 2 自帶的 IDLE 快速測試一兩行代碼,但在我的 Mac 下的 IDLE 交互模式里輸入中文會出現報錯:

>>> '中文'

Unsupported characters in input

這個問題在 v2ex 上同樣有同學問過: /t/44975 ,而他是在 Windows 下出現的,所以這個問題可能是普遍的。我原本以為這個問題同樣是因為上述的 stdin/stdout/stderr 的編碼問題而造成,就想順便解決掉。然而即使設置全局環境變量 PYTHONIOENCODING 為 utf-8 后仍舊不管用,IDLE 里輸入中文還是會報錯,sys.stdin.encoding 編碼還依舊是 us-ascii。

后來搜索后發現,貌似這個問題是由 IDLE 輸入輸出的內部實現機制導致的,可能跟 stdin/stdout/stderr 沒有關系。根據這里所說,IDLE 的交互模式下會根據機子的本地語言環境設置來判斷編碼,再用其對輸入進行轉換后再執行,而在我的 Mac 下這個編碼是 ascii,所以導致了 Unsupported characters in input。

而我搜到了一個可行的解決方法,其通過在 IDLE 的 IO 相關源碼(lib/python2.7/idlelib/IOBinding.py)中插入一行代碼強行覆蓋變量 encoding 的值為 'utf-8' 來解決這個問題。

不過后來經過我測試后發現,在 Mac 下其實更為簡單的一個解決方法是,設置 IDLE 的環境變量 LANG 為 "en_US.UTF-8"。同樣我不想通過 launchctl 設置全局環境變量來解決,而我采用的解決方法是:

打開編輯 IDLE.app/Contents/MacOS/IDLE 文件。

在大概第 24 行的地方插入一行設置環境變量 LANG 的語句:

+ os.environ["LANG"] = "en_US.UTF-8" # 第 24 行

os.environ["PYTHONEXECUTABLE"] = executable

os.environ["DYLD_LIBRARY_PATH"] = libdir

保存文件,重新打開 IDLE 就可以在其交互模式里輸入中文了。

總結

以上是生活随笔為你收集整理的python3 rid1.7.4.2 控制台中文乱码_TL;DR - 有关 Python 2 和 Sublime Text 中文 Unicode 编码问题的分析与理解...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。