讀取檔案
假設你已經在某個資料夾下建立了 “test.txt” 檔案,且裡面有一些內容,那你在當前位置輸入 Python3,進入到互動模式,然後執行下面的操作:
>>> f = open(`test.txt`)
>>> for line in f:
... print(line)
...
My name is Rocky
I love Python
複製程式碼
這裡提醒大家注意一下,如果是在該檔案所在的位置啟動的 Python 互動模式,那麼按照上面的方法 open(‘test.txt’) 開啟檔案,這意味著 test.txt 是在當前資料夾下的,如果要開啟其它資料夾下的檔案,要使用相對路徑或者絕對路徑來表示,從而讓 Python 能找到那個檔案。
看上面的例子,open() 一個檔案,即生成了一個物件,把這個物件賦值給變數 f,從而讓變數 f 和檔案物件之間建立了引用關係,接下來用 for 迴圈讀取檔案中的內容,把讀到的檔案中的每行賦值給變數 line (這裡的每行可以看作是物件),從列印的結果看,每一行與你讀取的檔案的每一行是相同的。
如果你做完了上述操作,那麼請看下面的操作:
>>> for line1 in f:
... print(line1)
...
>>>
複製程式碼
你會奇怪的發現,竟然什麼也沒有,是不是出錯了?其實並沒有,因為之前已經讀取過一次檔案的內容了,並且到了檔案的末尾,再重複操作,就要從文章的末尾開始讀了,當然就沒有什麼東西了,在 Python 中並不會認為這是錯誤。如果你想再次讀取的話,請重新 open() 一下檔案。
建立檔案
讀檔案只是針對檔案的操作之一,還有建立檔案。
在上面讀檔案的時候,我們開啟的是一個已經存在的檔案,那麼如何建立一個新檔案呢?請看下面的操作:
>>> new_file = open(`new.txt`,`w`)
>>> new_file.write(`this is a new file`)
18
>>> new_file.close()
複製程式碼
new_file = open(‘new.txt’,’w’) 意味著在當前的目錄下建立了一個名為 new_file 的檔案,該檔案為 “w” 開啟模式。
new_file.write(`this is a new file’) 則是向已建立的新檔案中寫入 “this is a new file”,並且返回的是寫入字串的長度。
new_file.close() 則是關閉當前檔案,然後將寫入的話儲存到檔案中。
由上面的例子我們可以看出,建立檔案我們同樣用的是 open() ,但是多了個 “w” ,這是告訴 Python 用什麼模式開啟檔案。在 Python 中,可以用很多不同的模式開啟檔案,請看下圖(截圖來自菜鳥教程):
從上圖中可以看出,不同的模式下開啟檔案可以進行不同的讀寫操作,如果什麼都不寫的話,預設為以只讀(r)的方式開啟檔案。
使用 with 自動關閉檔案
在前面的操作中我們可以看到,在對檔案進行寫操作之後,要執行關閉檔案的操作,執行關閉檔案的操作是為了將寫入的內容儲存到檔案中,如果不進行 close() 操作的話,那麼新寫入的內容將不會被儲存。
Python 早就知道會有很多馬大哈們會忘記 close()的操作,所以它建立了一種簡單的方法,這就是 with,它會實現自動關閉檔案,看!Python 是多麼的人性化!請看下面的操作:
>>> new_file.close()
>>> with open(`new.txt`,`a`) as f:
... f.write(‘
with is good good good.`)
...
23
複製程式碼
在這裡 with 其實是要發起一個語句的,這裡兼具了後面我會講的 try/finally,即可以在遇到異常的時候發出提醒,此處暫時先不講,等以後我再細說,我們先學會用 with。
>>> with open(`new.txt`) as f:
... print(f.read())
...
this is a new file
with is good good good.
複製程式碼
看吧,在 with 中我們可以放輕鬆的扔掉 close 了,上面例子中用到的 read() 方法,在明天的文章中我會講到。
檔案的屬性
很多時候,我們需要獲取一個檔案的有關屬性,比如檔案的建立日期,修改日期等等,在 Python 中有一個專門針對時間設計的模組 — time。請看下面的操作:
>>> import time
>>> time.localtime()
time.struct_time(tm_year=2018, tm_mon=7, tm_mday=25, tm_hour=21, tm_min=49, tm_sec=32, tm_wday=2, tm_yday=206, tm_isdst=0)
複製程式碼
其實還有一種辦法可以檢視檔案的這些屬性,就是 os 模組裡的 stat,在這裡我就是提一嘴,之後講到 os 模組的時候再仔細說。
讀取檔案的內容
因為檔案的物件是可迭代的,所以能夠用open() 開啟檔案,所以用 for 迴圈可以將檔案的內容讀出來。我在前面的文章說過,可以用 dir() 檢視檔案物件的屬性和方法,當你看了以後你會發現有 3 個方法 read / readline / readlines,單單從名稱上看,它們應該和讀有關係,事實上確實是這樣的,但是它們 3 個又有些微的差別。
1.read()
檔案物件的 read() 方法,其實完整的寫出來其實是 read( size ),只不過裡面的引數可以省略,如果不省略,則讀取檔案中的 size 個字元並返回一個字串;如果省略的話,則讀取檔案物件的字元知道 EOF,EOF == End – of – file。
>>> f = open(`new.txt`)
>>> f.read(10)
`this is a `
>>> f.read()
`new filewith is good good good.’
複製程式碼
如果你是按照上述的例子依次進行操作的,就會在 f.read() 後出現上述的結果,這主要是因為在前面已經 read(12) 了,指標已經移動到了第 12 個字元後面。
2.readline() & readlines()
readline() 就是它表面的意思,逐行讀取檔案的內容。
>>> f = open(`new.txt`)
>>> f.readline()
`this is a new filewith is good good good.`
>>> f.readline()
``
複製程式碼
每次執行 readline() 的時候它只讀一行,直到最後一行,如果還執行 readline() 的話,它不會報錯,返回的是空字串。
同樣也是有 readline(size) 的,如果給 readline(size) 引數,則讀取相應行的 size 個字元,有興趣的可以自己試一下。
還有一個是 readlines(),它的作用是將檔案中各行讀出來,放到一個列表中返回。
>>> f = open(`test.txt`)
>>> f.readlines()
[`My name is Rocky
`, `I love Python’]
複製程式碼
既然返回的是一個列表,那麼就能用 for 迴圈讀取列表元素,再觀察一下可以發現,列表中的每個元素都是檔案的一行,並且是字串。
>>> f = open(`test.txt`)
>>> for line in f.readlines():
... print(line)
...
My name is Rocky
I love Python
複製程式碼
這個是不是讓你覺得和上面檔案的 for 迴圈很類似?
>>> f = open(`test.txt`)
>>> for line in f:
... print(line)
...
My name is Rocky
I love Python
複製程式碼
乍一看兩種方式好像沒有什麼區別,其實這兩種方式是不同的。在 for line in f 中,並沒有將檔案中所以的行都讀入記憶體,而 for line in f.readlines() 中先執行了 f.readlines(),在記憶體中有一個列表,列表中包含了所有檔案的行,這就是兩種方式的區別。
大檔案的讀取
上面的三個讀取檔案內容的方法 read 和 readlines 都是一次性將全部的內容讀入記憶體,如果檔案不是很大的話,這種做法能夠保證讀取的速度,但是如果檔案內容很大,大到差不多記憶體那麼大或者更大的時候,就不能這麼做了。但是 Python 早就替你考慮到了,Python 中有一個 fileinput 模組,可以使用它來操作。
>>> import fileinput
>>> for line in fileinput.input(`test.txt`):
... print(line,end = ``)
...
My name is Rocky
I love Python
複製程式碼
因為我沒有大的檔案,只是為了演示一下這個模組的用法,對於這個模組更多的內容,可以在互動模式下用 dir() 去檢視。
seek
不知道你有沒有奇怪過在之前的演示中,每次都要做 f = open(‘***’) 類似的操作,否則就會出現下面的情況:
>>> f = open(`test.txt`)
>>> for line in f:
... print(line)
...
My name is Rocky
I love Python
>>> for line in f:
... print(line)
...
>>>
複製程式碼
是不是發現,當我們第二次迴圈檔案的時候,既沒有報錯,也沒有顯示檔案的內容,類似的現象在前面的 readline 中也出現過,這是因為讀取檔案的時候,有指標隨著運動,當讀取結束時,指標就到了相應的位置。
當指標結束運動時,可以使用 tell() 告訴我們當前指標的位置。
>>> f = open(`test.txt`)
>>> f.readline()
`My name is Rocky
`
>>> f.tell()
17
複製程式碼
現在我們來看 seek() 的能力,它能夠根據偏移量來移動指標。
>>> f.seek(0)
0
複製程式碼
上面的意思是將指標移動到檔案的開始,如果用 f.readline() 讀取的話,現在輸出的應該是檔案的第一行:
>>> f.readline()
`My name is Rocky
’
複製程式碼
其實還可以操縱指標到任何一個位置,請看下面的操作:
>>> f.seek(10)
10
>>> f.tell()
10
複製程式碼
f.seek(10) 就是將位置定位到從開頭算起到第 10 個字元後面,這時候如果使用 readline 的話,讀取的是從當前位置到行末的字母。
寫在之後
更多內容,歡迎關注公眾號「Python空間」,期待和你的交流。