本文 AI 產出,尚未審核

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

主題:讀取整個檔案 / 行迴圈


簡介

在日常開發中,檔案讀寫是最常見的 I/O 任務之一。無論是處理日誌、分析 CSV、或是載入設定檔,都必須先把磁碟上的文字資料讀進程式記憶體。掌握「一次讀取整個檔案」與「逐行迴圈」兩種方式,能讓你在效能、記憶體使用與程式可讀性之間取得平衡。

本篇文章將以 Python 為例,說明如何安全、有效率地讀取檔案內容,並提供實務範例、常見陷阱與最佳實踐,讓初學者能快速上手,同時也給中級開發者一些進階的觀點。


核心概念

1. 開啟檔案的基本語法

# 以唯讀模式開啟檔案,使用 with 讓檔案自動關閉
with open('data.txt', mode='r', encoding='utf-8') as f:
    # 在此區塊內可以對 f 進行讀寫操作
    pass
  • mode='r':唯讀模式,最常用於讀取文字檔。
  • encoding='utf-8':指定編碼,避免中文亂碼。
  • with 陳述式會在區塊結束時自動呼叫 f.close()避免檔案資源泄漏

2. 讀取整個檔案:read()

with open('data.txt', 'r', encoding='utf-8') as f:
    content = f.read()          # 直接把檔案全部讀入一個字串
    print(content[:100])        # 只印出前 100 個字元作為示範
  • read() 會一次把檔案全部讀入記憶體,適合小檔案(幾 MB 以內)。
  • 若檔案過大,會一次佔用大量 RAM,可能導致程式卡頓或記憶體不足。

3. 逐行讀取:readline()for line in f

3.1 使用 readline()

with open('data.txt', 'r', encoding='utf-8') as f:
    while True:
        line = f.readline()
        if not line:            # 讀到 EOF 會回傳空字串
            break
        print(line.rstrip())   # 去除行尾換行符號
  • 每次只讀取 一行,記憶體消耗固定。
  • 需要自行管理迴圈結束條件,程式碼較冗長。

3.2 使用迭代器 for line in f(最常見)

with open('data.txt', 'r', encoding='utf-8') as f:
    for idx, line in enumerate(f, start=1):
        # 直接在迴圈內處理每一行
        print(f'第 {idx} 行: {line.strip()}')
  • for 迴圈會把檔案物件當作 可迭代,Python 內部自動呼叫 readline()
  • 讀取速度與 readline() 相同,但程式碼更簡潔,且支援 enumeratezip 等便利工具。

4. 讀取固定大小的區塊:read(size)

with open('bigfile.bin', 'rb') as f:   # 二進位模式
    chunk_size = 1024 * 1024           # 1 MB
    while True:
        chunk = f.read(chunk_size)
        if not chunk:                  # 已讀完
            break
        # 這裡可以對 chunk 做處理,例如計算雜湊值
        print(f'讀取了 {len(chunk)} 位元組')
  • 這種方式適合 大型二進位檔(影像、影片、資料庫備份),可避免一次載入過大導致記憶體耗盡。

5. 同時讀取與寫入:r+w+a+

# 讀取舊內容、在檔案尾端追加新資料
with open('log.txt', 'a+', encoding='utf-8') as f:
    f.seek(0)               # 移到檔案開頭,先讀取舊內容
    old = f.read()
    print('舊內容長度:', len(old))

    f.write('\n新增一筆紀錄:' + '2025-11-20')
  • a+ 允許 讀取追加r+ 允許 讀寫(不會自動截斷),w+ 會先清空檔案再寫入。
  • 使用前務必了解每個模式的行為,以免不小心覆寫重要資料。

常見陷阱與最佳實踐

陷阱 說明 解決方案
忘記關閉檔案 直接 open() 而未 close(),會導致檔案鎖定或資源泄漏。 使用 with 陳述式自動關閉。
編碼不一致 讀取 UTF‑8 檔卻使用預設編碼,會出現 UnicodeDecodeError 明確指定 encoding='utf-8'(或其他正確編碼)。
一次讀入過大檔案 使用 read() 讀取 GB 級檔案,會耗盡記憶體。 改用 逐行固定區塊 讀取方式。
行尾換行符號未處理 print(line) 會多出空白行。 使用 line.rstrip()line.strip()
在寫入模式下誤用 read() wa 模式只能寫,讀取會拋出 UnsupportedOperation 依需求選擇正確模式(r+a+)。

最佳實踐小結

  1. 永遠使用 with:保證檔案在例外發生時也能正確關閉。
  2. 先判斷檔案大小:小檔案可直接 read(),大檔案則採用逐行或區塊方式。
  3. 統一編碼:在團隊專案中,建立「檔案編碼規範」並在程式中明確指定。
  4. 避免硬編碼路徑:使用 os.path.join()pathlib.Path 產生跨平台路徑。
  5. 例外處理:捕捉 FileNotFoundErrorPermissionError,提供友善錯誤訊息。
from pathlib import Path

def read_first_n_lines(filepath: Path, n: int = 10):
    try:
        with filepath.open('r', encoding='utf-8') as f:
            return [next(f).strip() for _ in range(n)]
    except FileNotFoundError:
        print(f'檔案不存在:{filepath}')
    except PermissionError:
        print(f'沒有讀取權限:{filepath}')

實際應用場景

場景 為何使用逐行讀取 範例程式碼
日誌分析 日誌檔通常很大,只需一次處理一行,避免記憶體爆炸。 for line in open('app.log'): if 'ERROR' in line: ...
CSV 大資料集 逐行解析每筆紀錄,配合 csv.DictReader 可直接產生字典。 import csv\nwith open('data.csv') as f:\n for row in csv.DictReader(f):\n ...
文字搜尋工具 只要找到第一筆符合條件的行,就可提前結束迴圈。 for i, line in enumerate(f):\n if keyword in line: print(i); break
二進位檔案分割 影片或備份檔案常需分塊上傳或加密。 參考「讀取固定大小的區塊」示例。
設定檔載入 設定檔通常只有幾十行,用 read() 直接取得全部內容最簡單。 config = json.loads(open('config.json').read())

總結

  • 一次讀取整個檔案 (read()) 適合小檔案,程式簡潔但記憶體需求高。
  • 逐行迴圈 (for line in freadline()) 是處理大檔案的首選,能保持低記憶體使用並保持程式可讀性。
  • 固定區塊讀取 (read(size)) 讓你在二進位或超大型檔案上自由控制 I/O 效能。
  • 使用 with、明確編碼、適當的檔案模式以及例外處理,是避免常見陷阱的關鍵。

掌握以上技巧後,你就能在 Python 中自如地處理各種檔案讀取需求,從簡單的文字檔到龐大的日誌、CSV、甚至二進位資料,都能寫出安全、效能佳且易於維護的程式碼。祝你寫程式順利,檔案操作無懈可擊!