Python 初學第十二講—檔案處理
利用程式進行讀/寫檔案的處理
在撰寫程式的過程當中,目前為止我們大多是使用鍵盤來進行輸入( input()
函式),或是利用螢幕本身的 console
輸出(搭配 print()
函式)來處理輸入輸出。
然而,我們並不可能總是使用鍵盤來進行輸入,假如今天的輸入值太多、或是我們要處理的是一些已經存在的數據,利用鍵盤輸入就顯得十分不效率;相對的,我們得到程式的輸出值以後,如果要將結果傳給其他人,那麼使用螢幕輸出就不會是一個好方法。
因此,這時我們所需要的,就是檔案處理的方法。
開啟以及關閉檔案
當我們今天想要來處理檔案,首先要先讓電腦知道現在我們所要處理的檔案是哪一份檔案。此時我們所使用的方法如下:
f = open(檔名, mode)
檔案物件 file object
此時變數 f
被 assign 的是一個 file object,所謂的 file object 的說明如下:
A file object allows us to use, access and manipulate all the user accessible files. One can read and write any such files. — — GeeksforGeeks
也就是說, file object 就是一個 Python 當中的物件,提供許多函式、方法讓我們處理任意的檔案。
開啟檔案 open()
open(file, mode='模式')
在這裡所用到的函式 open()
的部分通常包含兩個參數,檔案名稱以及模式 mode
。
檔名所要填入的是檔案路徑,指的是相對路徑,也就是相對於你的程式執行的位置而言,你的檔案在什麼地方。如果在同一個資料夾當中執行,可以直接寫檔案的名稱,如 "test.txt"
。
而 mode
代表的是你開啟檔案的模式,Python 提供了數種開啟檔案的模式,包含僅供讀取、可以覆寫檔案等等。以下將對於其中三種稍作簡介:
- “r” 唯讀模式:當我們以
mode
為 “r” 開啟檔案時,意即我們只能從指定的檔案讀取資料,並不能夠對這個檔案的內容進行更動。與此同時,如果我們所指定的檔案不存在,將會產生FileNotFoundError
的例外。 - “w” 寫入模式(覆寫):而如果我們以 “w” 為開啟檔案的模式時,意即要來進行檔案的寫入,會在開啟的位置直接覆蓋掉原本的檔案。如果我們所指定的檔案路徑/名稱不存在,會新增一個新的檔案。
- “a” 寫入模式(續寫):但若我們設定開啟檔案的模式為 “a”,是指在此模式下開啟檔案要進行寫入時,會從原本的檔案最後繼續進行寫入。
關閉檔案 close()
當我們在程式當中開啟了檔案以後,如果要停止對於這個檔案的更動或寫入,可以將檔案關閉。此時所使用的函式如下:
f.close()
當我們要開啟其他檔案時,當然可以直接再開啟然後取代掉目前的 file object,但是記得在使用時關閉檔案依然是一個好習慣。
讀取檔案
接著,當我們成功開啟檔案以後,下一個要做的就是將檔案當中的資料讀取進來。Python 在 file object 當中提供了數種從檔案讀取資料的方法,供大家做選擇使用,以下將對於幾種不同的用法稍作簡介。
file.read()
第一種是 f.read()
函式,用法如下:
file.read([size])
括號當中的參數 size
是 optional 的,當我們設置了 size
的值,電腦就會自動讀到指定的字節數量,若沒有設置就會將整個檔案都讀取進來。
舉例來說,當我們今天有一個叫做 file_io.txt
的文字檔,內容如下:
I love Python!
ccClub number one!
我們嘗試使用 f.read()
來讀取資料,並且嘗試以下兩種不同的讀取方式:
words = f.read()
print(words)
以及
word = f.read(6)
print(word)
分別以有無限制讀取字節數量的方法進行讀取,執行結果如下:
由圖可見,當我們執行的是 f.read(6)
,我們所讀取到的內容為 I love
,共六個字節;但當我們執行 f.read()
時,讀取到的即是完整的資料內容: I love Python\n ccClub number one!
。
file.readline()
另一種方法是 f.readline()
,他所做的事情跟這個函式的命名相同,就是將讀取檔案中的整行資料,但是一次只讀取一行,包含 \n
字元,如果檔案當中包含了 N
行的資料,我們就必須呼叫 f.readline()
N 次。
我們再一次讀取剛才的文字檔 file_io.txt
,並且將兩行內容輸出。為了證明 f.readline()
真的只會讀取一行,我們將輸出順序顛倒:
first_line = f.readline()
second_line = f.readline()
print(second_line, first_line, sep = "\n")
執行結果如下:
file.readlines()
除了逐行讀取,Python 也提供一次讀取所有資料的方法:
f.readlines()
f.readlines()
會將檔案當中的所有資料都逐行讀取進來,然後會將其回傳成為一個 list
,我們嘗試執行以下的程式碼,將檔案使用 f.readlines()
讀取進來以後,用變數 k 接住,然後將其印出以便查看:
f = open('file_io.txt')
k = f.readlines()
print(k)
f.close()
執行結果如下:
由此可見, f.readlines()
會將檔案當中每一行當作是一個字串,然後存進串列當中回傳。因此 for loop
經常與 f.readlines()
搭配使用,方法如下:
for line in f.readlines():
print(line)
執行結果如下,由於當 f.readlines()
將每一行的文字讀取進來時,會連換行符號的 \n
一起讀進來,因此,在使用 print()
將其印出時,會有原本字串當中的 \n
以及 print()
本身預設的 end
字元 \n
,所以中間會出現空行:
除此之外, Python 還給了我們一個幾乎一模一樣的用法,在不使用 f.readlines()
的情況下,我們可以直接利用 for loop
對於我們的 file object f
來 iterate,方法如下:
f = open('file_io.txt')
for line in f:
print(line)
f.close()
我們會得到相同的執行結果:
寫入檔案
講完如何讀取檔案,接著將介紹該如何將資料寫入檔案。與讀取相同,Python 同樣也提供了幾種不同的輸出方式:
file.write()
第一種是 f.write()
,其概念相對於剛才所介紹的 f.read()
,參數的資料型態是字串 string
,在使用上也相當容易:
舉例的程式碼如下:
f = open('file_io.txt', 'w')
f.write("Try to use file.write()\nHail HYDRA")
f.close()
執行以上的程式碼,將會得到一個新的檔案內容:
現在檔案的內容完全就是剛才我們作為 f.write()
參數的字串。
file.writelines()
當然,如果你不想要每輸出一段東西就使用一次 f.write()
,Python 也提供了另一個概念和 f.readlines()
相對的函式: f.writelines()
用法如下:
file.writelines(seq)
這裡的參數 seq
指的是 f.writelines()
的參數必須是一個序列,也就是 list
或是 tuple
這類的資料型態。而 f.writelines()
就會將你所給的 sequence 當中的內容印出,舉例來說,假如我們今天想要輸出和上面相同的檔案,我們可能的寫法如下:
f = open('file_io.txt', 'w')
seq = ["Try to use file.write()\n","Hail HYDRA"]
f.writelines(seq)
f.close()
而執行上面的程式碼,我們將會得到如下的文字檔:
由此可見,雖然 f.writelines()
會將我們所給的參數 sequence 當中的內容一個一個印出,卻不會像 print()
一樣預設在輸出的東西後面附上換行 \n
,因此,我們必須在要換行的字串中自行加上 \n
。
print()
當然,貼心的 Python 也提供了我們利用熟悉的 print()
就可以完成輸出到檔案的方法。當我們今天想要將任何東西輸出時,都會直接使用 print()
,然後頂多更改參數 sep
或是 end
。然而,除了這兩個參數以外,還有第三個參數 file
。
舉例來說,我們今天有一個 list 叫做 movielist ,內容如下:
movielist=["The Shawshank Redemption",
"The Godfather",
"The Dark Knight",
"12 Angry Men",
"Schindler's List",
"The Lord of the Rings: The Return of the King",
"Pulp Fiction",
"Fight Club"]
當我們對這個 list 直接使用 print 時,會得到如下的結果:
然而,當我們設定了 file
這個參數以後,將程式碼改成下方的樣子,就不一樣了:
f = open('movies.txt','w')
print(movielist, file = f)
f.close()
執行結果如下:
我們得到了一個裝著和方才直接使用 print()
時一樣內容的 txt
檔案。
在檔案中移動位置
接著,我們要來介紹如何更進一步的進行檔案的處理。當我們在進行檔案的讀取時,有時可能會想要將檔案從頭到尾讀取第二遍。
假設今天有一個文字檔叫做 sample.txt
,內容如下:
123
456
789
那麼,當我們想要將上面的檔案內容印出兩次時,可能會寫出如下的程式碼進行以下的操作:
f = open('sample.txt')
for line in f:
print(line)
for line in f:
print(line)
f.close()
在我們的想像當中,上面的程式碼在第一個 for loop 跑完以後,下一個 for loop 應該要將檔案的內容在輸出一次,也就是說,我們心裡想的輸出結果應該要是這樣子的:
123456789123456789
然而,執行結果卻如下:
很顯然的,第二次的 for loop 完全沒有印出東西來。
原因是這樣子的,當我們在讀取或是寫入檔案時,可以想像成電腦裡面有一個指標,類似於我們在使用 word 或是任何的文字編輯器時的指標,我們會依照他的位置,知道我們接下來如果要打字會出現在什麼地方。
同樣的,當 file object 在處理檔案時也是一樣的概念。在上面的範例當中,第一個 for loop 結束以後,這個指針隨著我們的讀取已經到了文件的最後面。因此,接下來想要再使用 for loop 來讀取的話,如果我們沒有將指針移到檔案最前面,就無法得到任何東西。因此,以下要介紹用來移動這個指針或是獲取現在指針位置的方法。
file.seek()
第一個是 f.seek()
,顧名思義,就是在檔案當中尋找。不過這裡說的尋找指的並不是尋找某個字或是某個資料,而是尋找某個位置,用法如下。
file.seek()
我們繼續使用上面的例子,此時,若我們想要將檔案的內容完整輸出兩次,只需要在兩個 for loop 之間加上一行 f.seek(0)
即可:
f = open('sample.txt')
for line in f:
print(line)
f.seek(0)
for line in f:
print(line)
f.close()
執行結果如下圖:
也就是說,當我們想要移動這個指針的位置,只要使用 f.seek()
就可以將指針移到目標位置。所以當我們將 f.seek()
當中的參數設為 1
時,就會輸出下面的結果:
可以看出,當我們寫 f.seek(1)
時,將會移到文件當中的位置一。所以當我們在讀取時,會從第二個字元開始讀取。
需要注意的是,如果檔案內容有中文字,可能就需要去更改檔案開啟時的編碼方式等等,在此便不贅述。
file.tell()
最後,我們要來介紹的是 f.tell()
。他和 f.seek()
接近於相對的概念,所謂的 f.tell()
,顧名思義就是會回傳給你現在指標所在的位置。
為了舉例方便,我們同樣使用方才的 sample.txt
,內容如下:
123
456
789
如果我們今天在讀取資料時使用的是 f.read()
時,我們可以很輕鬆地知道目前指針的位置在哪裡。然而,若我們想要搭配 f.seek()
或是其他方法使用時,就需要知道目前確切的指針位置,與此同時也可以知道目前進行到檔案的什麼地方。
舉例來說,當我們今天先使用了 f.read()
又使用了 f.readline()
時,可能會想要知道接下來會從哪個位置開始。試寫程式碼如下:
f = open('sample.txt')
print(f.read(1))
print(f.readline())
print(f.tell())
f.close()
而執行結果如下:
由此可見,使用 f.tell()
可以得到目前指針所在的位置,是下一行的開頭也就是 123\n
之後的位置 4
。
小結
本篇簡介了許多檔案處理相關的方法,雖然是以 txt
檔為介紹範例,但是對於其他檔案也是可以使用的,最多就是依照目標檔案的編碼格式進行調整。
當然,針對不同的檔案格式, Python 也有許多集前人智慧的好用 module 可以使用,這個就稍待之後再來進行介紹。
我們是 ccClub 團隊,致力於讓 Python 成為大家的第二外語,希望能用淺顯易懂、循序漸進的方式,帶領新手一步步跨入程式設計的世界。如果你喜歡這篇文章,請給我們 1~10 個掌聲。
如果你喜歡「Python初學」的教學系列文,請給我們 11 個以上的掌聲。
Facebook: ccClub Python讀書會
基礎線上課程:跟著商管女孩一起學 Python