前言
3. 檔案的開啟與關閉 介紹了open()的檔案名稱與模式兩個參數。open()其實還有第三個常用參數 —— 編碼 (encoding)。
open(檔案名稱 [, 模式] [, 編碼])open函式全部有 8 個參數,最常使用的是檔案名稱、模式和編碼參數,其中只有第一個檔案名稱是不可省略,其他的參數都可以省略,省略時會使用預設值。
中文資料在讀寫檔案時最容易出問題,而問題幾乎都出在編碼上。本節說明編碼的選擇與常見錯誤。
編碼參數
指定檔案的編碼模式,一般可設定cp950或UTF-8(大小寫都可以)。
| 編碼 | 說明 |
|---|---|
cp950 | 繁體中文 Windows 的預設編碼,也就是記事本所稱的 ANSI |
UTF-8 | 國際通行的編碼,Linux/macOS 與多數程式的預設 |
UTF-8-sig | UTF-8 但會自動處理 BOM(見後面說明) |
如果是用 Spyder 儲存的檔案,預設編碼為UTF-8;若是繁體中文 Windows 系統,編碼依作業系統而定,預設編碼是cp950。
可以在.py程式中用下列程式取得目前作業系統設定的編碼:
import locale
print(locale.getpreferredencoding())省略 encoding 會怎樣
在中文 Windows 系統中開啟一個純文字檔,用「記事本」編輯的話,預設是用 ANSI 編碼儲存,因此可以使用下列語法開啟 ANSI 編碼的檔案:
f = open('file1.txt', 'r') # 使用系統預設編碼
f = open('file1.txt', 'r', encoding='cp950') # 明確指定 cp950不指定 encoding 是「不可攜」的寫法
省略
encoding時,Python 會使用作業系統的預設編碼 —— 這代表同一支程式在你的電腦跑得好好的,換到同學的電腦(或 Linux 主機)可能就亂碼。
建議一律明確寫出encoding='UTF-8',不要依賴系統預設。
編碼不符會發生什麼事
如果使用encoding='cp950'去讀取 UTF-8 編碼的檔案,顯示資料內容會出現錯誤。
[簡例] 使用 cp950 開啟已另存為 UTF-8 編碼格式的<file2.txt>檔並顯示資料內容。(檔名:filereadUTF-8.py)
[結果]
UnicodeDecodeError: 'cp950' codec can't decode byte 0xbf in position 2:
illegal multibyte sequence程式碼:
filereadUTF-8.pyf = open('file2.txt', 'r', encoding='cp950') # ❌ 檔案其實是 UTF-8 for line in f: print(line, end="") f.close()
必須將encoding指定為UTF-8才可順利讀取和顯示:
f = open('file2.txt', 'r', encoding='UTF-8') # ✅由於國際間通行的編碼以及許多 Linux 系統預設都是使用 UTF-8 編碼,因此 建議將檔案另存為 UTF-8(不要使用 ANSI)。
認得這個錯誤訊息
錯誤 意義 通常原因 UnicodeDecodeError讀的時候解不開 讀檔的 encoding 跟檔案實際編碼不符 UnicodeEncodeError寫的時候轉不出去 要寫的字元該編碼不支援(例如用 cp950 寫 emoji) 看到
Decode就往「讀檔」找,看到Encode就往「寫檔/輸出」找。
BOM 的處理
什麼是 BOM
BOM (Byte Order Mark) 是一個特殊字元,功能是用來標示文件的編碼。BOM 是在 Windows 系統中用「記事本」將檔案儲存為 UTF-8 時會自動產生的。
[簡例] 讀取 UTF-8 編碼的<file2.txt>檔案的文件內容。(檔名:fileread6.py)
[結果]
['123中文字\n', 'abcde\n', 'hello\n']
123中程式碼:
fileread6.pywith open('file2.txt', 'r', encoding='UTF-8') as f: doc = f.readlines() print(doc) with open('file2.txt', 'r', encoding='UTF-8') as f: str1 = f.read(5) print(str1) # 123中
有沒有注意到,串列內容資料最前面多了一個「」字元,這個字元就是 BOM。
BOM 會佔 1 個字元,因此f.read(5)的結果只看到「123中」這 4 個字,因為第一個字元 BOM 未顯示出來。
實測驗證:
utf-8-sig 寫出的檔案前 3 bytes: b'\xef\xbb\xbf'
utf-8 寫出的檔案前 3 bytes: b'123'
用 utf-8 讀有 BOM 的檔 -> '123中文字\n'
用 utf-8-sig 讀有 BOM 的檔 -> '123中文字\n'BOM 最麻煩的地方是「看不見」
這個 BOM 字元因為未顯示出來,在資料處理時經常會造成誤判。典型的災情:
line = f.readline().strip() if line == "123中文字": # ❌ 永遠不成立! ...因為實際的值是
'123中文字',比對字串、轉數字 (int())、當字典的鍵都會出錯,而且print 出來看起來完全正常,極難察覺。
解法一:用 UTF-8-sig 讀取
讀取有 BOM 的文件檔時,明確地加上encoding='UTF-8-sig',如此會將 BOM 與文件分離單獨處理。
[結果]
['123中文字\n', 'abcde\n', 'hello\n']
123中文程式碼:
fileread7.pywith open('file2.txt', 'r', encoding='UTF-8-sig') as f: doc = f.readlines() print(doc) with open('file2.txt', 'r', encoding='UTF-8-sig') as f: str1 = f.read(5) print(str1) # 123中文
BOM 消失了,read(5)也正確讀到 5 個字元。
⭐ 讀檔一律用
UTF-8-sig最安全實測確認:
UTF-8-sig讀「沒有 BOM」的檔案也完全正常。用 utf-8-sig 讀無 BOM 的檔 -> '123中文字\n' ← 沒問題也就是說,讀取時用
UTF-8-sig有 BOM 會自動去掉、沒 BOM 也不會出錯,是最保險的選擇。
但寫入時要用UTF-8(用UTF-8-sig會主動寫入 BOM,反而給別人添麻煩)。
動作 建議編碼 讀取 UTF-8-sig寫入 UTF-8
解法二:用編輯器去除 BOM
有經驗的程式設計師會用其他的文件編輯器,如 NotePad++,選擇「編碼 → UTF-8 碼」(而不是「UTF-8 碼(BOM 檔首)」)來去除 BOM。
相關主題與延伸閱讀
- 0. 章節索引:本章全部筆記的學習地圖與快速查找。
- 3. 檔案的開啟與關閉:
open()的檔案名稱與模式參數。 - 4. 文字檔資料的寫入與讀取:讀寫函式的實際用法。
- 10. 二進位檔案的讀寫:二進位模式不需要
encoding參數。 - 5. 例外處理:
UnicodeDecodeError的捕捉與處理。