Python 課程 – 檔案操作(File I/O)
主題:開啟與關閉檔案(open、with)
簡介
在日常開發中,讀寫檔案是最常見的需求之一。無論是處理日誌、儲存使用者設定,抑或是批次匯入/匯出資料,都離不開檔案的開啟與關閉。Python 內建的 open() 函式提供了彈性十足的介面,而 with 陳述式則讓我們在使用完檔案後自動釋放資源,避免遺漏關閉檔案所造成的資源泄漏或資料遺失。
本篇文章將從 基本概念、實作範例、常見陷阱 以及 最佳實踐 逐層說明,幫助初學者快速上手,同時讓有一定經驗的開發者重新檢視自己的寫法,提升程式的可靠度與可讀性。
核心概念
1. open() 的基本用法
open() 會回傳一個 檔案物件(file object),其語法如下:
file = open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
file:檔案路徑(相對或絕對)。mode:開啟模式,最常用的有'r'讀取(預設)'w'寫入(會覆寫原檔)'a'附加(寫入時不會清空)'b'二進位模式(如'rb'、'wb')'+'讀寫同時(如'r+'、'w+')。
encoding:文字檔的編碼,常見utf-8、cp950(繁體中文 Windows 預設)等。
小技巧:在處理跨平台文字檔時,建議明確指定
encoding='utf-8',避免因預設編碼不同而產生亂碼。
2. 手動關閉檔案:file.close()
f = open('data.txt', 'r', encoding='utf-8')
content = f.read()
print(content)
f.close() # 必須手動關閉
若忘記呼叫 close(),檔案會保持「開啟」狀態,直到 Python 的垃圾回收機制自行釋放。這在大量檔案操作或長時間執行的服務端程式中,可能導致 檔案描述符耗盡,從而拋出 OSError: [Errno 24] Too many open files。
3. 使用 with 陳述式自動關閉
with 會在區塊結束時自動呼叫檔案物件的 __exit__ 方法,等同於 try…finally 的寫法,讓程式更安全且易讀。
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content) # 離開 with 區塊後,檔案已自動關閉
優點:
- 保證關閉:即使區塊內發生例外,檔案仍會被正確關閉。
- 語法簡潔:不必額外寫
try…finally。 - 可疊加:同時開啟多個檔案,只要使用逗號分隔即可。
with open('input.txt', 'r', encoding='utf-8') as fin, \
open('output.txt', 'w', encoding='utf-8') as fout:
for line in fin:
fout.write(line.upper())
4. 常見的開啟模式範例
| 模式 | 說明 | 例子 |
|---|---|---|
'r' |
只讀,檔案必須存在 | open('a.txt') |
'w' |
寫入,若檔案已存在會被清空 | open('b.txt', 'w') |
'a' |
附加寫入,檔案不存在時會自動建立 | open('c.txt', 'a') |
'rb'/'wb' |
二進位讀寫,常用於圖片、音訊等 | open('img.png', 'rb') |
'r+' |
讀寫,同時允許讀取與寫入 | open('d.txt', 'r+') |
'x' |
獨占寫入,檔案已存在則拋出 FileExistsError |
open('e.txt', 'x') |
程式碼範例
以下提供 5 個實用範例,從最基礎的讀寫到稍微進階的例外處理與二進位操作,均以 註解 說明每一步的意圖。
範例 1:最簡單的文字讀取(手動關閉)
# 讀取 data.txt,內容以 UTF-8 解碼
f = open('data.txt', 'r', encoding='utf-8')
try:
for line in f:
print(line.rstrip()) # 去除換行符號
finally:
f.close() # 確保檔案一定被關閉
重點:使用
try…finally可以保證即使迴圈內發生例外,檔案仍會被關閉。
範例 2:使用 with 寫入檔案(自動關閉)
# 將一串文字寫入 output.txt,若檔案已存在會被覆寫
with open('output.txt', 'w', encoding='utf-8') as fout:
fout.write('Hello, Python!\n')
fout.write('這是一行中文。\n')
# 不需要額外的 close()
範例 3:逐行讀寫並轉換大小寫(同時開兩個檔案)
# 讀取 input.txt,將每行文字轉成大寫後寫入 output.txt
with open('input.txt', 'r', encoding='utf-8') as fin, \
open('output.txt', 'w', encoding='utf-8') as fout:
for line in fin:
fout.write(line.upper())
範例 4:二進位檔案的複製(圖片、音訊等)
# 複製 image.png 為 image_copy.png,使用二進位模式
with open('image.png', 'rb') as src, open('image_copy.png', 'wb') as dst:
while True:
chunk = src.read(4096) # 每次讀 4KB,減少記憶體使用
if not chunk:
break
dst.write(chunk)
範例 5:捕捉例外並提供友善訊息
import sys
file_path = 'maybe_missing.txt'
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = f.read()
print(data)
except FileNotFoundError:
print(f'⚠️ 找不到檔案:{file_path}', file=sys.stderr)
except UnicodeDecodeError as e:
print(f'⚠️ 編碼錯誤:{e}', file=sys.stderr)
說明:
FileNotFoundError與UnicodeDecodeError是最常見的兩種例外,適時捕捉能讓使用者得到明確的錯誤資訊,而不會直接中斷程式。
常見陷阱與最佳實踐
| 陷阱 | 為何會發生 | 解法 / 最佳實踐 |
|---|---|---|
| 忘記關閉檔案 | 手動 open() 後未呼叫 close(),或在例外發生時直接跳出 |
使用 with,或在 try…finally 中呼叫 close() |
| 檔案編碼不一致 | 在 Windows 常見使用 cp950,而 Linux 預設 utf-8,導致讀取時出現亂碼或 UnicodeDecodeError |
明確指定 encoding(如 utf-8),或使用 errors='ignore'/'replace' 處理異常字符 |
| 一次讀取過大檔案 | 使用 read() 直接把整個檔案載入記憶體,檔案過大會耗盡記憶體 |
逐行或分塊讀取(for line in f:、f.read(4096)) |
| 檔案路徑硬編碼 | 程式只能在特定目錄執行,移植性差 | 使用 os.path.join()、pathlib.Path 來組合路徑 |
| 寫入模式不當 | 用 'w' 開啟已存在的檔案,卻不小心把舊資料覆蓋了 |
若要保留舊資料,改用 'a'(附加)或先檢查檔案是否存在 |
| 二進位/文字模式混用 | 在二進位模式下寫入文字字串會拋出 TypeError |
二進位模式只能寫入 bytes,文字模式只能寫入 str |
其他最佳實踐
- 使用
pathlib:更直觀且跨平台。from pathlib import Path p = Path('logs') / 'app.log' with p.open('a', encoding='utf-8') as log_file: log_file.write('程式開始執行\n') - 設定緩衝區:對於大量小寫入,可調整
buffering參數提升效能。with open('big.txt', 'w', buffering=1) as f: # 行緩衝 for i in range(100000): f.write(f'{i}\n') - 使用
shutil.copyfileobj進行檔案複製,省去自行寫迴圈的麻煩。import shutil with open('src.bin', 'rb') as src, open('dst.bin', 'wb') as dst: shutil.copyfileobj(src, dst, length=8192)
實際應用場景
| 場景 | 可能的程式碼片段 | 為何需要 open/with |
|---|---|---|
| 日誌系統 | 每次服務請求寫入 app.log |
使用 with open(..., 'a') 確保即時寫入且不會遺漏關閉 |
| 資料匯入/匯出 | 讀取 CSV、JSON、XML 檔案 | with open(..., 'r', encoding='utf-8') as f: 搭配 csv、json 模組 |
| 設定檔管理 | 讀取 .ini、.yaml 並寫回修改 |
讀取時使用 with,寫入時使用 'w' 或 'r+' 以保證原子性 |
| 檔案備份 | 每晚自動備份資料庫匯出檔 | 二進位模式 ('rb'/'wb') + shutil.copyfileobj |
| 大型文字處理 | 文字分析、關鍵字統計 | 逐行讀取 (for line in f:) 減少記憶體佔用,with 確保檔案釋放 |
總結
open()是 Python 操作檔案的入口,模式與編碼是最常需要注意的參數。- 永遠使用
with來管理檔案生命週期,能自動關閉檔案、避免資源泄漏,程式碼也更具可讀性。 - 針對 大型檔案、二進位資料 或 跨平台編碼,要善用分塊讀寫、
pathlib、以及明確的encoding設定。 - 了解常見陷阱(忘記關閉、編碼不一致、一次讀取過大等)並遵守最佳實踐,能讓你的檔案操作更安全、效能更佳。
掌握了 開啟與關閉檔案 的正確方式,就為日後的資料處理、日誌紀錄、設定管理等各種實務需求奠定了堅實的基礎。祝你在 Python 的檔案操作旅程中寫出乾淨、可靠且高效的程式碼!