我有以下兩行 Python (v. 3.10.7) 程式“stdin.py”:
import sys
print(sys.stdin.read())
以及以下單行文本檔案“ansi.txt”(CP1252 編碼),其中包含:
‘I am well’ he said.
請注意,打開和關閉引號分別是0x91和0x92。在 Windows-10 cmd 模式下,Python 代碼的行為符合預期:
python stdin.py < ansi.txt # --> ‘I am well’ he said.
另一方面,在 Windows Powershell 中:
cat .\ansi.txt | python .\stdin.py # --> ?I am well? he said.
顯然,CP1252 字符在 Python/PowerShell 組合中被視為不可列印字符。如果我在“stdin.py”中用檔案輸入替換標準輸入,Python 會正確地將 CP1252 引號字符列印到螢屏上。PowerShell 本身可以正確識別和列印,0x91并且0x92.
問題:有人可以向我解釋為什么 cmd 與 PowerShell 結合 Python 的作業方式不同嗎?為什么 Python 不能識別 CP1252 引號字符 0x91以及0x92當它們通過 PowerShell 輸入時?
uj5u.com熱心網友回復:
tl;博士
使用$OutputEncoding偏好變數:
- 在Windows PowerShell中:
# Using the system's legacy ANSI code page, as Python does by default.
# NOTE: The & { ... } enclosure isn't strictly necessary, but
# ensures that the $OutputEncoding change is only temporary,
# by limiting to the child scope that the enclosure cretes.
& {
$OutputEncoding = [System.Text.Encoding]::Default
"‘I am well’ he said." | python -c 'import sys; print(sys.stdin.read())'
}
# Using UTF-8 instead, which is generally preferable.
# Note the `-X utf8` option (Python 3.7 )
& {
$OutputEncoding = [System.Text.UTF8Encoding]::new()
"‘I am well’ he said." | python -X utf8 -c 'import sys; print(sys.stdin.read())'
}
- 在PowerShell(核心)7 中:
# Using the system's legacy ANSI code page, as Python does by default.
# Note: In PowerShell (Core) / .NET 5 ,
# [System.Text.Encoding]::Default` now reports UTF-8,
# not the active ANSI encoding.
& {
$OutputEncoding = [System.Text.Encoding]::GetEncoding([cultureinfo]::CurrentCulture.TextInfo.ANSICodePage)
"‘I am well’ he said." | python -c 'import sys; print(sys.stdin.read())'
}
# Using UTF-8 instead, which is generally preferable.
# Note the `-X utf8` option (Python 3.7 )
# NO need to set $OutputEncoding, as it now *defaults* to UTF-8
"‘I am well’ he said." | python -X utf8 -c 'import sys; print(sys.stdin.read())'
筆記:
$OutputEncoding控制用于通過管道(到標準輸入)將資料發送到外部程式的編碼。它在Windows PowerShell中默認為 ASCII(!),在PowerShell (Core)中默認為UTF-8 。[Console]::OutputEncoding控制從外部程式(通過標準輸出)接收的資料如何被解碼。它默認為控制臺的活動代碼頁,而后者又默認為系統的舊版 OEM 代碼頁,例如437在美國英語系統上)。
不幸的是,這兩種編碼默認不對齊。雖然Windows PowerShell不會再看到任何變化,但PowerShell (Core)還是有希望的:讓它始終默認為 UTF-8 是有意義的:
GitHub 問題 #7233建議至少將啟動 PowerShell 的快捷方式檔案默認為 UTF-8(代碼頁
65001);GitHub 問題 #14945更普遍地討論了有問題的不匹配。在 Windows 10 及更高版本中,有一個切換到 UTF-8系統范圍的選項,這使得 OEM 和 ANSI 代碼頁都默認為 UTF-8 (
65001);但是,這會產生深遠的影響,并且自 Windows 11 起仍被標記為處于測驗階段- 請參閱此答案。
背景資料:
首$OutputEncoding選項變數決定了 PowerShell 使用何種字符編碼來通過管道將資料(從 PowerShell 7.3 開始始終為text)發送到外部程式。
請注意,這甚至適用于從檔案中讀取資料的情況:從v7.3 開始,PowerShell 從不通過管道發送原始位元組:它首先將內容讀入 .NET 字串,然后根據
$OutputEncoding發送它們重新編碼它們到外部程式的管道。因此,輸入檔案使用什么編碼
ansi.txt最終是無關緊要的,只要 PowerShell在將其讀入 .NET 字串(內部由 UTF-16 代碼單元組成)時對其進行正確解碼。有關更多資訊,請參閱此答案。
因此,存盤的字符編碼$OutputEncoding必須與目標程式期望的編碼相匹配。
默認情況下,編碼與控制臺的活動代碼頁所隱含的$OutputEncoding編碼無關(其本身默認為系統的舊版 OEM 代碼頁,例如437在美國英語系統上),這至少是舊版控制臺應用程式傾向于使用的編碼;但是,Python 沒有,而是使用舊的ANSI代碼頁;其他現代 CLI,尤其是 NodeJS' node.exe,總是使用 UTF-8。
雖然PowerShell (Core) 7 $OutputEncoding中的默認值現在是 UTF-8,但遺憾的是, Windows PowerShell的默認值是 ASCII(!),這意味著非 ASCII 字符會“有損”地轉譯為逐字ASCII字符,即你看到了什么。?
因此,您必須(暫時)設定$OutputEncoding為 Python 期望的編碼和/或要求它改用 UTF-8。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/532934.html
