寫程式一開始,我們的世界都很「短暫」
變數一跑完就消失、輸出一印完就沒了!
但真正的應用不是這樣的。
你可能想要記錄使用者登入紀錄、分析一堆資料、保存遊戲進度、
或把 AI 結果匯出成 CSV 報表...等
這些都需要「檔案處理(File Handling)」。
學會它,代表你能讓程式:
本篇會從最基礎的 open()
、read()
、write()
開始,
一路帶你掌握 文字檔、CSV、DictWriter、seek/tell 等常見操作,
再搭配實作練習,讓你不只是「會開檔」,而是真正懂得如何和外部世界對話的工程師。
那我們就先從「開起檔案」說明吧!
open()
的基本用法與回傳:
f = open(file, mode='r', buffering=-1, encoding=None, ...)
光是這一行code,我們就有很多重點可以來介紹!一起來看看!
file
:檔名(可以是相對路徑或絕對路徑)。mode
:開啟檔案模式。最重要的參數(決定讀、寫、追加、二進位等)。下方會詳細說明。encoding
:文字編碼(預設在 Windows 可能是 cp950;建議明確指定 encoding='utf-8'
)。常用的編碼字串:ascii,big5,utf8buffering
0
:不緩衝(binary mode only)1
:行緩衝(newline based)>1
:指定緩衝區大小(bytes)1
(預設):依系統預設我們這邊來介紹一下mode的種類:(很重要!)
'r'
:讀取(預設)。檔案不存在會噴錯。'w'
:寫入(覆蓋原檔)。若檔案不存在會建立新檔。'a'
:追加(append)。內容會接在檔尾。'x'
:建立新檔(若檔已存在則失敗)。'+'
:更新(可讀可寫,結合上面某種模式,例如 'r+'
)。'b'
即為二進位模式(例如 'rb'
、'wb'
)。範例:open('data.txt', 'w', encoding='utf-8')
假設 f = open('file.txt', 'r', encoding='utf-8')
:
f.read(size=-1)
:讀取 size 個字元(size 預設 -1 表示讀完整個檔案)。
f.readline(size=-1)
:讀到換行(包含換行符)或 EOF,回傳單行字串。size 可限制最多讀多少字元。f.readlines()
:把檔案每行放到 list 回傳(每個元素含換行符)。for line in f:
—— 這是最常用、記憶體友善的逐行讀法範例:
在做範例之前,要先去創建一個.txt檔案
創好之後,再執行以下程式碼:
這邊做個小提醒,如果怕程式找不到檔案,可以先用絕對路徑!
filename = input('請輸入讀取檔名:')
fin = open(filename)
line = fin.readline()
while line:
print(line, end='')
line = fin.readline()
輸出:
成功讀取的話,就會看到程式把檔案內容列印出來!
write(s)
:把字串 s
寫進檔案(傳回寫入的字元數)。注意:write 需要字串;對其他型別先 str()
。print(..., file=f)
:利用 print
的 file
參數輸出到檔案(自動加換行,且更方便)。flush()
:把緩衝內容立刻寫入磁碟(通常用於長時間執行、要即時寫入紀錄時)。close()
:關閉檔案並釋放資源(若忘記呼叫,可能導致資料未寫入或檔案鎖住)。推薦作法:使用 with open(...) as f:
可以自動關閉檔案(context manager),不需要手動 close()
(文件資源管理協定:__enter__
, __exit__
)。
範例:
poem = '''床前明月光
疑是地上霜
舉頭望明月
低頭思故鄉
'''
try:
print(poem, file=open('output.txt', 'w'), flush=True)
print('資料寫出至output.txt')
except Exception as e:
print('資料寫出失敗:', e)
輸出:
可以到存取路徑的地方去查看有沒有成功喔!
建議路徑可以先訂在桌面(最好找!)
假設我們要再追加內容至剛剛的檔案要如何實作呢?(利用'a')
這邊一樣建議在路徑上可以使用絕對路徑!
poem = '''故人西辭黃鶴樓
煙花三月下揚州
孤帆遠影碧空盡
惟見長江天際流
'''
try:
with open('output.txt', 'a') as f:
f.write(poem)
print('資料成功新增至output.txt')
except Exception as e:
print('資料新增失敗:', e)
成功新增內容的話,也可以實際打開檔案是否成功喔!
tell()
:回傳目前檔案讀寫位置(以位元/字元為單位,取決於模式)。seek(offset, from=0)
:設定檔案指標位置。from
可以是:
0
:從檔頭起算(預設),1
:從目前位置為起點,2
:從檔尾起算。這邊一樣先創一個檔案(裡面的內容可以自行決定)
這邊我就打一下我很愛的雞湯語錄~
f=open('Believe.txt', 'r')
str = f.read(32)
print ("讀取的字串是 : ", str)
position = f.tell()
print ("目前位置 : ", position)
position = f.seek(34, 0)
str = f.read(60)
print ("重新調整後讀取的字串是 : ", str)
position = f.tell()
print ("目前位置 : ", position)
f.close()
輸出:
CSV(Comma-Separated Values)= 以逗號分隔的純文字表格。
幾乎所有試算表軟體都支援(Excel、Google Sheets)。
特性:
1.純文字格式,可用記事本打開
2.每行是一筆資料(record)
3.每欄用逗號分隔(field)
4.第一行通常是欄位名稱(header)
5.可跨平台交換資料
6.若欄位含逗號/換行,需加引號包住
把每一列變成 list(記得要先import csv)
假設我們現在有一個csv檔案(data.csv):
import csv
file = open('data.csv', 'r')
csvCursor = csv.reader(file)
for row in csvCursor:
print(row)
file.close()
輸出:
也可以試試看這種指定索引的方式:
import csv
file = open('data.csv', 'r')
csvCursor = csv.reader(file)
for row in csvCursor:
print(row[0],row[1],row[2],row[3],row[4],row[5],row[6])
file.close()
輸出:
delimiter
:欄位分隔符(預設 ,
)quotechar
:引用字元(預設 "
)lineterminator
:換行符(預設 \r\n
)quoting
:引用模式(csv.QUOTE_MINIMAL
, csv.QUOTE_ALL
, csv.QUOTE_NONNUMERIC
, csv.QUOTE_NONE
)strict
:格式錯誤是否拋例外提醒:CSV 的字元編碼要和讀取程式一致,否則 Excel/記事本打開會亂碼(特別是 Big5 / CP950 與 UTF-8 的差異)。如果是要給 Excel(繁體 Windows)打開,可能需要 encoding='cp950' 或使用 Excel 的匯入功能指定編碼。
csv.DictWriter:以 dict 寫入,並可 writeheader()
寫出標頭。
舉例:
csv.DictReader:把每一列變成 dict(欄位名為 key)
import csv
file = open('data.csv', 'r')
csvCursor = csv.DictReader(file)
for row in csvCursor:
print(row['name'], row['score'])
file.close()
輸出:
假設我要把分析結果、學生名單、銷售報表寫回 CSV!」
這時候,我們就會使用 csv.writer()
物件。
語法說明:
語法 | 說明 |
---|---|
open('檔名', 'w', newline='', encoding='utf-8') |
w 代表「寫入模式」。newline='' 可避免多出空白行。 |
csv.writer(f) |
建立一個「寫入器(writer)」物件。 |
writer.writerow(可疊代物件) |
寫入一行資料(list、tuple 都可以)。 |
writer.writerows(多筆資料) |
一次寫入多行資料(傳入巢狀 list)。 |
範例:
import csv
with open('result.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['name', 'age', 'city']) # 寫入一行
writer.writerow(['Alice', 20, 'Taipei'])
writer.writerow(['Bob', 22, 'Kaohsiung'])
成功後,就可以到該儲存路徑看到結果喔!
範例2:
import csv
header = ['name', 'address', 'tel', 'email', 'age']
data = [['Sean', '123, Oak Street, Taipei', '02-9876-5432', 'sean@gmail.com', 40],
['Amy', '456, Park Avenue, Taichung', '04-8877-6655', 'amy@gmail.com', 30],
['David', '789 First Road Tainan', '06-654-3210', 'david@gmail.com', 25]]
file = open('contact.csv', 'w')
csvCursor = csv.writer(file)
csvCursor.writerow(header)
for d in data:
csvCursor.writerow(d)
file.close()
print('聯絡人輸出至contact.csv')
輸出:(看到成功輸出後,就可以開啟檔案檢查!)
打開檔案可以看見成功寫入的內容:
前面我們學過 csv.writer()
,它是把 list 或 tuple 寫進 CSV。
但在實務中,資料往往長這樣:
students = [
{'name': 'Alice', 'math': 88, 'english': 92},
{'name': 'Bob', 'math': 75, 'english': 85},
{'name': 'Cathy', 'math': 95, 'english': 98}
]
這是「字典格式」的資料(dictionary),
每一筆都是一個學生、裡面有多個欄位。
若要寫成 CSV,我們可以使用更方便的工具 —— csv.DictWriter()
。
語法說明:
語法 | 說明 |
---|---|
csv.DictWriter(f, fieldnames=[欄位名清單]) |
建立字典寫入器(key 會對應欄位名稱) |
writeheader() |
自動根據 fieldnames 寫出表頭 |
writerow(dictionary) |
寫入一筆字典資料 |
writerows(list_of_dict) |
一次寫入多筆字典資料 |
範例:
結合我們先前所學,我們也可以自己輸入資料,自動存成csv檔案!
import csv
fieldnames = ['product', 'price', 'stock']
with open('products.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
while True:
product = input("輸入商品名稱(或輸入 q 結束):")
if product.lower() == 'q':
break
price = float(input("輸入價格:"))
stock = int(input("輸入庫存量:"))
writer.writerow({'product': product, 'price': price, 'stock': stock})
print("✅ 已寫入一筆資料!")
print("資料已儲存至 products.csv")
輸出:
儲存成功後,可以到該路徑查看products.csv:
錯誤狀況 | 問題說明 | 解法 |
---|---|---|
忘記 writeheader() |
結果第一列變成資料而不是欄位名。 | 在寫資料前一定要呼叫一次。 |
字典 key 跟 fieldnames 不一致 |
會漏掉或多出欄位。 | 確保所有 key 名稱與 fieldnames 完全相同。 |
中文亂碼 | 在 Windows 上開啟會變亂碼。 | 用 encoding='utf-8-sig' 。 |
今天是第26天~這幾天一直在思考要怎麼把想要教給讀者的知識,
濃縮在這三天~真的好難取捨啊啊!!
每次在撰寫文章的時候,看到最底下「已輸入8000字,,,9000字,,,10000字!!」
都會覺得讀者會不會其實覺得我很撈叨?話太多!哈哈哈~
不過在這邊也真的很佩服每位鐵人們!
我自己也當過螢幕前的讀者,現在更能了解,或許讀者花不到幾分鐘就看完的文章,
我們可能耗費了3小時以上在撰寫(like me!)~
回歸正傳,
我們今天學了很多檔案處理的技巧,這其實很重要!
也對明天我們要進行的數據分析做一個預告~
學會檔案處理,就像是讓專案有「記憶」、「有延續性」,
再也不用害怕,電腦一關掉資料都不見的心痛感~
那麼我們就明天見囉!!辛苦了!!