Python 出現 cp950 codec can't decode:讓 Windows 上的 Python 預設用 UTF-8 讀寫文字

摘要

在 Mac 上能正常執行的 Python 腳本,放到繁體中文的 Windows 讀寫中文或 emoji 時,常因為兩邊的系統編碼不同而出現 cp950 codec 的錯誤。只要替 Windows 帳號設定一次環境變數來開啟 Python 的 UTF-8 模式,之後執行的 Python 都會預設用 UTF-8 讀寫,不用逐一修改腳本。

文章目錄

Python 讀寫文字時,如果程式沒有指定編碼,就會沿用作業系統的預設編碼。Mac 的預設編碼是 UTF-8,繁體中文 Windows 則是 cp950(微軟版本的 Big5),因此在 Mac 上能正常執行的腳本,放到 Windows 讀寫中文或 emoji 時,會出現 'cp950' codec can't decode 或 can't encode 的錯誤。我的專案都在 Mac 上開發,放到 Windows 讓 AI Agent 執行就出錯,每次都要手動在程式裡補上編碼。我們只要替 Windows 帳號設定一次環境變數來開啟 Python 的 UTF-8 模式,之後在這台電腦執行的 Python 都會預設用 UTF-8,同一份腳本不用修改就能在兩邊執行。

測試環境是 Windows 11 繁體中文版,終端機是 Windows Terminal 裡的 Windows PowerShell。示範的 report.py 和 Mac 上常見的寫法一樣,讀寫檔案時沒有指定編碼,它會讀取含有中文與 ☕ 的 UTF-8 檔案,整理出待辦事項後印出完成訊息。

cp950 錯誤訊息與 Python 版本

Python 3.14 以下的程式沒有指定編碼時,在繁體中文 Windows 上會用 cp950 讀寫文字,遇到 cp950 無法處理的內容時,依動作會出現三種錯誤。訊息裡的位元組與位置會隨內容不同:

錯誤訊息 發生的動作
UnicodeDecodeError: 'cp950' codec can't decode byte 0xe9 in position 2: illegal multibyte sequence 讀取 UTF-8 的文字檔
UnicodeEncodeError: 'cp950' codec can't encode character '\u2615' in position 2: illegal multibyte sequence 寫入的文字含有 cp950 沒有的字,例如 emoji
UnicodeEncodeError: 'cp950' codec can't encode character '\u2705' in position 3: illegal multibyte sequence print 的輸出交給 AI Agent 或其他程式接收

第三種錯誤只在輸出由其他程式接收時出現,因此我們自己在終端機執行正常,交給 AI Agent 執行才會失敗。在 Python 3.14 執行 report.py 時,讀取檔案的 read_text() 就會出現第一種錯誤:

Windows PowerShell 顯示 Python 3.14.3 執行 report.py,read_text 出現 UnicodeDecodeError: ‘cp950’ codec can’t decode byte 0xe9

Python 3.15 起依 PEP 686 預設開啟 UTF-8 模式,PEP 686 也說明這項改變主要影響 Windows 使用者。我們先在終端機執行 python --version 看腳本用的版本,再對照自己的情況:

情況 原因 實測結果 處理方式
python --version 是 3.14 以下 沒有指定編碼時用 cp950 3.10、3.12、3.14 執行 report.py 都出錯 開啟 UTF-8 模式
python --version 是 3.15 以上 預設用 UTF-8 3.15.1 執行 report.py 正常 不用設定
電腦裝了 3.15,仍出現 cp950 錯誤 專案用的是舊版,例如 uv 專案的 .python-version 固定在 3.12 uv run 執行的 3.12.13 沒有開啟 UTF-8 模式 用 uv run python --version 確認後開啟 UTF-8 模式
開啟 UTF-8 模式或改用 3.15 後,出現 'utf-8' codec can't decode 讀到 cp950 的資料 cmd /c ver 的輸出與 Big5 舊檔都讀不出來 讀取時指定編碼,見後面的「UTF-8 模式下的 cp950 資料」

UTF-8 模式的環境變數

版本在 3.14 以下時,在終端機執行這行指令,替目前的 Windows 帳號新增 PYTHONUTF8 環境變數,值設為 1:

1setx PYTHONUTF8 1

出現「成功: 已經儲存指定的值。」就完成了。這個變數存在目前帳號的使用者環境變數,不需要系統管理員權限,也不影響電腦上的其他帳號,Python 3.7 起的版本都會讀取它。

值只能是 1 或 0。寫成 true 這類其他值時,Python 會無法啟動並顯示 Fatal Python error: preconfig_init_utf8_mode: invalid PYTHONUTF8 environment variable value,這時再執行一次 setx PYTHONUTF8 1 就能改正。

確認 UTF-8 模式已生效

執行 setx 前就開著的終端機讀不到新的值。在同一個視窗執行這行指令確認 UTF-8 模式,印出 0 代表還沒開啟:

1python -c "import sys; print(sys.flags.utf8_mode)"

Windows PowerShell 執行 setx PYTHONUTF8 1 顯示成功: 已經儲存指定的值,同一個視窗印出 sys.flags.utf8_mode 仍是 0

關掉終端機,從開始功能表重新開啟後再執行一次,印出 1 就代表已經開啟,report.py 也能讀寫中文並印出 ✅:

重新開啟的 Windows PowerShell 中,Python 3.14.3 的 sys.flags.utf8_mode 是 1,report.py 印出整理完成:3 項待辦與 ✅

AI Agent 的應用程式也要重新開啟。實測設定前就開著的 Claude 桌面版,它執行的指令仍然讀不到 PYTHONUTF8,要把應用程式完全結束再重新開啟。

要還原時,在 PowerShell 執行這行刪除變數,再重新開啟終端機:

1[Environment]::SetEnvironmentVariable("PYTHONUTF8", $null, "User")

UTF-8 模式下的 cp950 資料

開啟 UTF-8 模式或改用 Python 3.15 之後,沒有指定編碼的程式反過來會把 cp950 的資料當成 UTF-8 解讀,並出現 'utf-8' codec can't decode byte 的錯誤。實測遇到兩種情況:

情況 錯誤訊息 處理方式
用 subprocess 讀取 ipconfig、cmd /c ver 這類 Windows 內建指令的輸出 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xaa in position 21: invalid start byte 加上 encoding="locale"
讀取以 Big5 存檔的舊檔案 UnicodeDecodeError: 'utf-8' codec can't decode byte 0xab in position 0: invalid start byte open() 加上 encoding="cp950"

Windows 內建指令在繁體中文 Windows 上仍然輸出 cp950。下面的 winver.py 用 cmd /c ver 取得 Windows 版本:

1import subprocess
2
3result = subprocess.run(["cmd", "/c", "ver"], capture_output=True, text=True)
4print(result.stdout.strip())

開啟 UTF-8 模式後執行,讀取輸出的執行緒先出現 UnicodeDecodeError,result.stdout 因此變成 None,接著 print 那一行又出現 AttributeError:

Python 3.14.3 在 UTF-8 模式執行 winver.py,先出現 UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xaa,再出現 AttributeError: ‘NoneType’ object has no attribute ‘strip’

在 subprocess.run 加上 encoding="locale" 後,Python 會用系統的編碼讀取指令的輸出,正確印出 Microsoft Windows [版本 10.0.26300.9711]。這個參數從 Python 3.10 起可以使用,有沒有開啟 UTF-8 模式都能正確讀取:

1result = subprocess.run(["cmd", "/c", "ver"], capture_output=True, text=True, encoding="locale")

實測紀錄

測試用 uv 安裝的 3.10.22、3.12.13、3.14.3,以及 python.org 的 Python install manager 安裝的 3.15.1。示範的 report.py 如下,notes.md 是一份會議紀錄:

1from pathlib import Path
2
3notes = Path("notes.md").read_text()
4todos = [line[2:] for line in notes.splitlines() if line.startswith("- ")]
5Path("todo.md").write_text("".join(f"[ ] {t}\n" for t in todos))
6print(f"整理完成:{len(todos)} 項待辦 ✅")

各版本執行其他動作的結果:

動作 3.10、3.12、3.14 預設 3.14 設定 PYTHONUTF8=1 3.15.1 預設
寫入 ☕ cp950 編碼錯誤 存成 UTF-8 存成 UTF-8
print emoji 到管線 cp950 編碼錯誤 正常 正常
subprocess 讀取 cmd /c ver 正常 UTF-8 解碼錯誤 UTF-8 解碼錯誤
讀取 Big5 的 CSV 正常 UTF-8 解碼錯誤 UTF-8 解碼錯誤

同一行 print 直接執行時正常,接上管線後就出現 cp950 編碼錯誤:

同一行 print 完成與 ✅ 的指令,直接執行時正常顯示,接上 Out-String 後出現 UnicodeEncodeError: ‘cp950’ codec can’t encode character ‘\u2705’

其他設定與執行方式的結果:

測試條件 結果
讓 Claude Code 用 3.14.3 執行 print("完成 ✅") cp950 編碼錯誤
3.14.3 只設定 PYTHONIOENCODING=utf-8 標準輸入、輸出與錯誤輸出改用 UTF-8,讀取 notes.md 仍是 cp950 錯誤
3.14.3 加上 -X utf8 只有那一次執行使用 UTF-8 模式
uv 專案固定在 3.12.13,設定 PYTHONUTF8=1 uv run 的 sys.flags.utf8_mode 從 0 變成 1
3.15.1 設定 PYTHONUTF8=1 與預設相同
3.15.1 設定 PYTHONUTF8=0 或加上 -X utf8=0 讀取 notes.md 又出現 cp950 錯誤
刪除 PYTHONUTF8 後新開的終端機 3.14.3 回到 0,3.15.1 仍是 1

總結

Mac 上寫的 Python 腳本在繁體中文 Windows 出現 cp950 錯誤時,替 Windows 帳號開啟一次 Python 的 UTF-8 模式,就不用逐一修改腳本。實測設定後重新開啟的終端機裡,原本讀取失敗的 report.py 能正常讀寫中文與 emoji。

延伸閱讀

Ted Liou

Ted Liou

現役軟體工程師,分享 Unity 與 AI 的技術開發經驗,也提供諮詢。

留言