Learning Objectives
- Python 內建函數 open() 的用法與開檔模式
- write()、read()、readline() 的用法,以及用 for 逐列讀取
- with 敘述的應用:自動關閉檔案
為什麼要讀寫檔案
到目前為止,程式的輸入來自 input()(標準輸入,預設是鍵盤),輸出交給 print()(標準輸出,預設是螢幕)。
程式一結束,變數裡的資料就不見了。要把資料保存下來、或讀取別人準備好的資料,就要用檔案。
f = open(檔名, 模式) # 開啟檔案,得到檔案物件
f.write(...) / f.read() # 寫入或讀取
f.close() # 關閉檔案
本課的範例會在工作目錄先放好 note.txt(內容是「中央大學」「ncu」「ncu.edu.tw」三列)。
程式執行後,輸出區的「檔案」分頁會顯示工作目錄裡的檔案內容。
範例統一使用 UTF-8 編碼的文字檔。在自己的電腦上,建議寫成
open('note.txt', 'r', encoding='utf-8'), 不然 Windows 可能用系統預設的編碼(例如 cp950)開啟,中文會變成亂碼。
開檔模式
open() 的第二個引數是模式,預設是 'rt'(讀取文字檔):
| 模式 | 說明 |
|---|---|
'r' |
開檔讀資料(預設);檔案不存在會出現 FileNotFoundError |
'w' |
開檔寫資料,會先清空檔案;檔案不存在就建立 |
'x' |
開檔寫資料,檔案已存在就失敗(FileExistsError) |
'a' |
開檔寫資料,附加在檔案結尾 |
'b' |
以二進位模式開啟(例如圖片),搭配其他模式寫成 'rb'、'wb' |
't' |
以文字模式開啟(預設) |
'+' |
開啟檔案做更新,可以讀也可以寫,例如 'r+' |
寫入檔案:w 與 a
write() 把字串寫進檔案,不會自動加換行,要換行就自己寫 '\n'。播完後打開輸出區的「檔案」分頁,看看 note.txt 的內容。把第 10 行的 'a' 改成 'w' 再跑一次,結果有什麼不同?
讀取檔案:read()
read(size) 讀 size 個字元並傳回字串;省略 size 就讀到檔案結尾。讀過的部分不會重讀:第二次 read() 從上一次停下來的地方繼續。
一次讀一列:readline()
readline() 每次讀一列,保留結尾的換行字元;讀到檔案結尾(EOF)時傳回空字串 ''。空字串在 if 裡是 False,所以 if not line: break 可以判斷讀完了。
用 for 逐列讀取,以及 readlines()
檔案物件可以直接放進 for,每一圈拿到一列,這是最常用的寫法。readlines() 則一次把所有列讀成串列。注意每一列結尾的 \n,通常會用 strip() 去掉。
追蹤檔案讀寫
下面的程式會印出什麼?
f = open("t.txt", "w")
f.write("ab")
f.write("cd\n")
f.close()
f = open("t.txt", "a")
f.write("ef")
f.close()
f = open("t.txt")
print(repr(f.read()))
f.close()
下面的程式會印出什麼?
f = open("t.txt", "w")
f.write("one\ntwo\n")
f.close()
f = open("t.txt")
print(repr(f.readline()), repr(f.readline()), repr(f.readline()))
f.close()
note.txt 原本有三列資料。執行 open("note.txt", "w") 之後立刻 close(),note.txt 會變成怎樣?
記得關檔:try/finally 與 with
開啟的檔案要 close():寫入的資料可能還在暫存區,沒關檔就不保證寫進去;開太多檔案也會耗盡系統資源。
問題是,讀寫途中發生例外,close() 那一行就執行不到了。傳統作法是把 close() 放進 finally;
with 敘述則把「準備」與「清理」包起來,離開區塊時一定會自動關檔,寫法更簡潔:
with open('note.txt', 'r') as fp:
data = fp.read()
print(data)
# 離開 with 區塊,fp 已經關閉
沒有 with:try/finally 自己關檔
先把 fp 設成 None,開檔失敗時 finally 才知道不用關。把第 4 行的檔名改成不存在的 nofile.txt 再跑一次,看看流程怎麼走(FileNotFoundError 屬於 OSError)。
with 敘述:自動關檔
with open(...) as fp: 開檔後把檔案物件指派給 fp。注意 fp.closed 在區塊裡是 False,離開區塊後變成 True。最後一段用 with 寫入 log.txt,執行後到「檔案」分頁查看。
想一想:with
下面的程式會印出什麼?
with open("t.txt", "w") as f:
f.write("hi")
print(f.closed, end=" ")
print(f.closed)
關於 with open(...) as f:,下面哪一個敘述正確?
實作:寫入成績再讀回來加總
程式已經把三個成績寫進 scores.txt(每列一個)。請用 with 開啟 scores.txt,逐列讀取、轉成整數後加總,最後印出 總分 255。
重點精華回顧
open(檔名, 模式)傳回檔案物件;常用模式'r'讀、'w'寫(先清空)、'a'附加、'x'新建(已存在就失敗),'b'二進位、'+'可讀可寫。write(字串)不會自動加換行;read(size)讀 size 個字元,省略就讀到結尾;readline()讀一列並保留\n,讀到結尾傳回''。- 用
for line in 檔案物件:逐列讀取最常見;readlines()一次讀成串列。 - 檔案用完要
close();有例外時用try/finally確保關閉。 with open(...) as f:離開區塊時自動關檔,即使中途發生例外,是讀寫檔案的建議寫法。- 在自己的電腦上,中文文字檔請加
encoding='utf-8'。