﻿# 如何讓 Python 程式正確的處理 UTF-8 字元編碼

最近在嘗試微軟最近推出的 [MarkItDown](https://github.com/microsoft/markitdown) 工具，它使用 Python 開發，可用於將各種文件檔案轉換為 Markdown 格式，支援的文件格式也非常多，這在 AI 盛行的時代是非常有用的。因為文件換為 Markdown 格式之後，可以讓大語言模型進行分析。不過我在 PowerShell 底下使用的時候，沒有意外的又出意外了，因為它無法正確的處理 UTF-8 字元的輸入輸出，所以只要遇到中文就掛掉，真的很煩，這種問題都不知道處理幾次了。這篇文章我打算來介紹如何讓 Python 程式正確的處理 UTF-8 字元。

![illustrating the complexities of handling UTF-8 encoding in Python  The scene depicts colorful, interwoven binary code st](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/8f51d2df90094af4113e-5a4db6bf-a4d4-4591-880f-b413b3e42089.webp)

### 問題重現

首先我們來看看問題是如何重現的，我們可以使用下面的程式碼來重現問題：

```bash
markitdown coding-guideline.pdf > coding-guideline.md
```

錯誤訊息如下：

```log
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Python312\Scripts\markitdown.exe\__main__.py", line 7, in <module>
  File "C:\Python312\Lib\site-packages\markitdown\__main__.py", line 39, in main
    print(result.text_content)
UnicodeEncodeError: 'cp950' codec can't encode character '\u26ab' in position 4123: illegal multibyte sequence
```

解決這個問題，最簡單的方法是改用 `-o filename` 參數來指定輸出檔案，這樣就不會有問題了：

```bash
markitdown coding-guideline.pdf -o coding-guideline.md
```

但我有不得不用 Pipe (`|`) 的理由，所以這個問題我必須解決！✊

### 解決方法 1：透過 `PYTHONUTF8` 環境變數

解決方法也很簡單，給個環境變數就好：

-   命令提示字元 (Command Prompt)
    
    ```bash
    set PYTHONUTF8=1
    set PYTHONIOENCODING=utf-8
    ```
    
-   PowerShell
    
    ```powershell
    $env:PYTHONUTF8=1
    $env:PYTHONIOENCODING='utf-8'
    ```
    
-   Shell
    
    ```powershell
    export PYTHONUTF8=1
    export PYTHONIOENCODING=utf-8
    ```
    

這樣就可以正確的處理 UTF-8 字元了，以下這段命令可以正常執行！👍

```bash
markitdown coding-guideline.pdf > coding-guideline.md
```

至於 `PYTHONIOENCODING` 跟 `PYTHONUTF8` 環境變數的區別如下：

-   `PYTHONUTF8`
    
    從 `Python 3.7` 開始，引入了 `PYTHONUTF8` 環境變數。當您將 `PYTHONUTF8` 設定為 `1` 時，Python 會啟用 `UTF-8` 模式，這意味著 Python 會在多數情況下預設使用 `UTF-8` 編碼，包括**檔案處理**、**標準輸入輸出**(`STDIN`, `STDOUT`)和**管道**(pipes)等。這有助於在不同的作業系統和地區設定中保持一致的編碼行為，減少因預設編碼不同而導致的問題。
    
-   `PYTHONIOENCODING`
    
    此環境變數用於指定 Python 在執行期間對標準輸入 (`stdin`)、標準輸出 (`stdout`) 和標準錯誤 (`stderr`) 的編碼方式。當您設定 `PYTHONIOENCODING` 為 `utf-8` 時，Python 會使用 UTF-8 編碼來處理這些標準流 (`STDIN`, `STDOUT`, `STDERR`)，這對於需要處理多語言文字的應用程式特別有用。需要注意的是，這僅影響標準**輸入**和**輸出**，對於使用 `open()` 函數讀寫檔案時的編碼，仍需在程式碼中明確指定 `encoding` 參數。
    

這意味著，當你使用的 Python 版本在 Python 3.7 以上時，你可以使用 `PYTHONUTF8` 環境變數來啟用 UTF-8 模式就好，設定 `PYTHONIOENCODING` 就顯得有點多餘。

**使用建議如下：**

-   如果您僅需要修改標準輸入輸出和錯誤的編碼，且希望指定特定的編碼，可以使用 `PYTHONIOENCODING`。
    
-   如果您希望整個 Python 環境預設使用 UTF-8 編碼，建議使用 `PYTHONUTF8`，這樣可以確保在各種操作中都使用 UTF-8 編碼，減少編碼不一致的問題。
    

需要注意的是，目前 Python 最新版為 `Python 3.13.1`，而預計從 `Python 3.15` 開始，將預設啟用 `UTF-8` 模式，這意味著未來版本的 Python 將自動使用 UTF-8 作為預設編碼。這對於處理 UTF-8 字元的問題，將會是一個很大的改進！🎉

### 解決方法 2：透過 `-X utf8` 參數

第二種解決方法，則是靠 `python` 指令的 `-X utf8` 參數來指定編碼，這樣也確實可以解決問題！

```bash
python -X utf8 -m markitdown
```

![終端機輸出顯示與非法多字元序列相關的 UnicodeEncodeError。錯誤發生在執行 Python 腳本時，並展示使用 UTF-8 編碼選項的建議修正。](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/788642008f16b33efb8f-e1a2818e-c509-49ee-bd05-48ef98b48133.jpg)

### 相關連結

-   [Python 3.13.1 documentation](https://docs.python.org/3/)
    -   [Command line and environment](https://docs.python.org/3/using/cmdline.html)
    -   [Python UTF-8 Mode](https://docs.python.org/3/library/os.html#utf8-mode)
-   [python - Checking that PYTHONIOENCODING is always "utf8" - Stack Overflow](https://stackoverflow.com/q/53516107/910074)
-   [PYTHONIOENCODING = UTF-8 引发的血案 - zhangyesmile - 博客园](https://www.cnblogs.com/zhangyesmile/p/11195656.html)
-   [分享幾個在 Windows 與 Linux 常見的編碼問題與解決方案](/post/2021/08/05/Character-Encoding-Problems-for-Windows-and-Linux)
