本文 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()相同,但程式碼更簡潔,且支援enumerate、zip等便利工具。
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() |
w、a 模式只能寫,讀取會拋出 UnsupportedOperation。 |
依需求選擇正確模式(r+、a+)。 |
最佳實踐小結
- 永遠使用
with:保證檔案在例外發生時也能正確關閉。 - 先判斷檔案大小:小檔案可直接
read(),大檔案則採用逐行或區塊方式。 - 統一編碼:在團隊專案中,建立「檔案編碼規範」並在程式中明確指定。
- 避免硬編碼路徑:使用
os.path.join()或pathlib.Path產生跨平台路徑。 - 例外處理:捕捉
FileNotFoundError、PermissionError,提供友善錯誤訊息。
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 f、readline()) 是處理大檔案的首選,能保持低記憶體使用並保持程式可讀性。 - 固定區塊讀取 (
read(size)) 讓你在二進位或超大型檔案上自由控制 I/O 效能。 - 使用
with、明確編碼、適當的檔案模式以及例外處理,是避免常見陷阱的關鍵。
掌握以上技巧後,你就能在 Python 中自如地處理各種檔案讀取需求,從簡單的文字檔到龐大的日誌、CSV、甚至二進位資料,都能寫出安全、效能佳且易於維護的程式碼。祝你寫程式順利,檔案操作無懈可擊!