本文 AI 產出,尚未審核

Python 課程 – 檔案操作(File I/O)

主題:開啟與關閉檔案(openwith


簡介

在日常開發中,讀寫檔案是最常見的需求之一。無論是處理日誌、儲存使用者設定,抑或是批次匯入/匯出資料,都離不開檔案的開啟與關閉。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-8cp950(繁體中文 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 區塊後,檔案已自動關閉

優點

  1. 保證關閉:即使區塊內發生例外,檔案仍會被正確關閉。
  2. 語法簡潔:不必額外寫 try…finally
  3. 可疊加:同時開啟多個檔案,只要使用逗號分隔即可。
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)

說明FileNotFoundErrorUnicodeDecodeError 是最常見的兩種例外,適時捕捉能讓使用者得到明確的錯誤資訊,而不會直接中斷程式。


常見陷阱與最佳實踐

陷阱 為何會發生 解法 / 最佳實踐
忘記關閉檔案 手動 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

其他最佳實踐

  1. 使用 pathlib:更直觀且跨平台。
    from pathlib import Path
    p = Path('logs') / 'app.log'
    with p.open('a', encoding='utf-8') as log_file:
        log_file.write('程式開始執行\n')
    
  2. 設定緩衝區:對於大量小寫入,可調整 buffering 參數提升效能。
    with open('big.txt', 'w', buffering=1) as f:   # 行緩衝
        for i in range(100000):
            f.write(f'{i}\n')
    
  3. 使用 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: 搭配 csvjson 模組
設定檔管理 讀取 .ini.yaml 並寫回修改 讀取時使用 with,寫入時使用 'w''r+' 以保證原子性
檔案備份 每晚自動備份資料庫匯出檔 二進位模式 ('rb'/'wb') + shutil.copyfileobj
大型文字處理 文字分析、關鍵字統計 逐行讀取 (for line in f:) 減少記憶體佔用,with 確保檔案釋放

總結

  • open() 是 Python 操作檔案的入口,模式編碼是最常需要注意的參數。
  • 永遠使用 with 來管理檔案生命週期,能自動關閉檔案、避免資源泄漏,程式碼也更具可讀性。
  • 針對 大型檔案二進位資料跨平台編碼,要善用分塊讀寫、pathlib、以及明確的 encoding 設定。
  • 了解常見陷阱(忘記關閉、編碼不一致、一次讀取過大等)並遵守最佳實踐,能讓你的檔案操作更安全、效能更佳。

掌握了 開啟與關閉檔案 的正確方式,就為日後的資料處理、日誌紀錄、設定管理等各種實務需求奠定了堅實的基礎。祝你在 Python 的檔案操作旅程中寫出乾淨、可靠且高效的程式碼!