Python入門基礎(7)——檔案讀取
序言:
1、不同程式語言讀寫檔案的操作步驟基本上是一致,大致可以分為以下幾個步驟:
(1)開啟檔案,獲取檔案描述符
(2)針對操作檔案描述符進行操作——讀/寫
(3)關閉檔案
2、值得注意的是,讀寫檔案操作完成以後,要及時關閉(和查詢資料庫連結是一致),雖然當前計算機,即便你不關閉也會產生宕機,但是及時關閉連結,是一個好習慣,原因分析一下:
- 檔案物件會佔據作業系統的資源,你不關閉,那麼會出現卡頓(當然對於目前計算機來說,不會明顯)
- 作業系統對同一時間能開啟的檔案描述符的數量是有限制的(也就是你同時開啟檔案的數量是有限制的)
- 如果不及時關閉檔案,還可能會造成資料丟失,因為我們吧資料寫入檔案時,作業系統不會立刻吧資料寫入磁碟,而是先把資料放到記憶體緩衝區,非同步寫入磁碟,當你關閉連結的時候,作業系統會保證把沒有寫入磁碟的資料全部寫入到磁碟中,否則可能會丟失資料
3、文字內容是:
趙錢孫李,周吳鄭王。
馮陳褚衛,蔣沈韓楊。
朱秦尤許,何呂施張。
孔曹嚴華,金魏陶姜。
一、讀寫模型
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
上面寫的程式碼,是一個開啟檔案的大致函式模型,open是用來開啟檔案的,裡面有很多引數,當然我們自己寫程式碼的時候,很多引數是沒有必要寫的(一般都是些前面兩個引數)。
上面最重要的引數只有前兩個:第一個file(指定了檔案路徑,其實就是一個字串);第二個mode(模型,分為只讀,只寫,可讀可寫,,,,,,)
上面的框框是所有mode形式,一般情況下,都是直接用前面兩種形式就可以了(r和w),要注意的是:
- 用w模式寫的時候,會將原文字內容清空,如果你想在原文字末尾寫入內容,就要用a模式
- r+會覆蓋原來的內容,比如你原來的檔案內容是“你好啊,朋友!”,寫入“上帝”後,就會變成“上帝啊,朋友!”,用上帝覆蓋住了你好
- a+增加了可讀性,但是其寫入內容的時候,不管你的檔案指標在哪裡都會在文字末尾寫入
1、簡單讀寫框架
(1)讀寫流程
知道了模型框架,那我們就先讀一個檔案:
# 第一步:(以只讀模式)開啟檔案 f = open('song.txt', 'r', encoding='utf-8') # 第二步:讀取檔案內容 print(f.read()) #將讀取的檔案內容打印出來,這裡輸出的格式和原檔案內容格式一模一樣(無論是換行還是空格) # 第三步:關閉檔案 f.close()
注意:這裡open開啟之後,讀取檔案,直接就轉換成了Unicode字串,這裡的read()方法,會一次性將檔案中所有的內容全部載入到記憶體中,這對於大部分情況是不合理的,比如一個檔案非常大,我們一次性讀取出來,就會把記憶體全部佔滿,那就沒有什麼意義了,下面介紹幾種讀檔案的方式:
上面介紹了幾乎所有的讀檔案方法,還有兩個與檔案指標位置相關的方法:
- seek(n)————將檔案指標移動到指定位元組的位置,具體函式可參看:seek用法
- tell()————獲取當前檔案指標所在位元組位置
(2)try .....finally......(better)
在實現基本功能的前提下,考慮一些可能的意外因素。因為檔案讀寫時都有可能產生IO錯誤(IOError),一旦出錯,後面包括f.close()在內的所有程式碼都不會執行了。因此我們要保證檔案無論如何都能被關閉。那麼可以用try...finally來實現,這實際上就是try...except..finally的簡化版:
try:
f = open('txt/test.txt', 'r', encoding='utf-8')
print(f.read())
num = 10 / 0
finally: #目的是無論上面有沒有錯,都要將open關掉
print('>>>>>>finally')
if f: #f為真,表示上面打開了檔案,所以下面要關掉
f.close()
輸出結果:
從上面這個圖上可以看出,雖然報了錯,但是最終還是輸出了:》》》》finally,這就說明最後的程式碼塊被執行了,也就是關閉了f,
(3)with,,,,(best!)
直接上程式碼例項:
with open('txt/test.txt', 'r', encoding='utf-8') as f: 這裡是賦值給f,當with結束以後,自動關閉
print(f.read())
print(f.closed)
結果顯示:可以看到最後輸出了true,表示f已經關閉了
為了避免忘記或者為了避免每次都要手動關閉檔案,我們可以使用with語句(一種語法糖,語法糖是為了簡化某些操作而設計的),with語句會在其程式碼塊執行完畢之後自動關閉檔案,就如上面寫的一樣。
注意:如果with程式碼塊中出現了問題,那麼這個問題就會被丟擲,需要我們自己處理(會中斷程式碼塊的執行),所以建議在必要的時候,在with語句外面套上一層try。。。except來捕獲和處理異常:
with open('txt/test.txt', 'r', encoding='utf-8') as f:
print(f.read())
num = 10 / 0 #程式碼塊裡面的錯誤之處
結果顯示:
對於這個結果,其實我感覺有些疑惑,根據程式碼應該是先將所有語句輸出了,然後再顯示錯誤才對啊,這個問題留作以後再解惑,如果有知道的讀者,可以在評論回答一下,絕對重謝。
2、讀例項
之前,把所有準備工作都做好了,接下來開始乾貨了,直接上相關程式碼例項。
(1)讀取指定長度的內容:
with open('txt/test.txt', 'r', encoding='utf-8') as f:
print(f.read(12)) #數字12就是指定長度,與編碼方式無關,就是返回12個字元
輸出:
注意:在這裡Python2與Python3還是有差別的,Python2中,數字12指的是位元組數,這就與編碼方式有關了,在一些編碼方式中,一個漢字佔3個位元組,所以在Python2中,會輸出4個漢字
(2)讀取檔案的一行內容:
with open('txt/test.txt', 'r', encoding='utf-8') as f:
print("輸出第一行內容:"+f.readline())
print("輸出第一行第一個字的內容:" + f.readline(1))
輸出結果:
好吧,上面顯示的結果和我預期有點不一樣:我之前以為是輸出的也是第一行內容,沒想到轉到第二行去了,後來才發現,原來文字讀取的時候,是有讀取位置的,第一行已經讀取完畢了,再次讀取的時候,不會重新開始讀,而是會在原有基礎上進行讀取。這也就是為什麼上面第二個輸出會顯示“馮”,而不是“趙”;本來想改一下,後來想想算了,當做警醒吧。
(3)遍歷檔案中的每一行
第一種方法是直接一次性讀取所有行到記憶體,然後遍歷列印:
with open('txt/test.txt', 'r', encoding='utf-8') as f:
for line in f.readlines():
print(line)
結果顯示:
有一個問題:就是每行文件輸出之間有一行空白區間,這個問題是因為讀取的檔案中每一行都預設有換行符,而print()方法也會輸出換行,因此兩個換行就會多出一個換行符(因為只需要1個就可以了),去掉空行也比較簡單:
- line.rstrip()——去除字串右邊的換行符(也就是從文字的角度去除)
- print(line,end = '')————避免print方法造成的換行
輸出結果顯示:
看,空白行被去除了
第二種方法:通過迭代器一行行讀取並列印
with open('txt/test.txt', 'r', encoding='utf-8', newline='') as f:
for line in f: #唯一的區別就在這裡,迭代器上一次已經說過了,當你不用的時候,它不會讀取,只有用的時候才會讀取
print(line,end = '') #這次去除一波換行符
顯示結果和上面是一樣的,同樣有一行空白區域
3、檔案讀取的一些其他方法
4、寫例項
原文字內容:
(1)簡單的寫入文字一句話
#寫操作
with open('Test.txt', 'w', encoding='utf-8') as f: #這裡定義的是w模式
print(f.write('你好'))
上面定義的是最簡單寫操作,但是注意這裡用的是w模式,操作執行的時候,首先會把原來的內容清空,然後在寫入,所以原本寫有4行百家姓的文字,變成了只有“你好”的文字:
(2)在原有文字後面新增內容
#寫操作
with open('Test.txt', 'a', encoding='utf-8') as f:
print(f.write('你好'))
這裡以a模式進行寫入,結果就在“你好”之後,再次寫入了“你好”(因為直接寫入以後,沒有進行更改,所以文字就不再是百家姓了):
(3)換行寫入
第一種方法:從文字最後位置著手
因為a模式是追加在文字最後一行的內容,但是這裡的最後一行,你是可以控制的,比如:
比如上面這個Test文字,只有4行,那麼最後寫入以後,就會在第4行後面新增,同樣是上面的程式碼執行以後:
然後,你更改一下文字,讓最後一行是空格:
這樣,最後的位置就變成了第5行,而第5行是空白字元,如果再次執行上面的程式碼,你就會發現結果不一樣了:
所以通過更改檔案“最後位置”可以實現換行輸入,這裡你要深入理解文字的最後一行是指什麼意思
第二種方法:從程式碼入手(best)
#寫操作
with open('Test.txt', 'a', encoding='utf-8') as f:
print(f.write('\n'+'你好')) #直接加入換行符,簡單高效
(4)在指定位置插入
#第一種方法:在第一行周前面插入666 with open('Test.txt', 'r', encoding='utf-8') as f: lines = f.readlines() #讀取檔案 print("讀取的文字:") print(lines) lines[0]=lines[0].replace("周","666周") #在第一行字串裡面的周前面加入666 s=''.join(lines) with open('Test.txt', 'w+', encoding='utf-8') as f: f.write(s) del lines[:] #清空列表 print("最後的文字:") print(lines)
上面這種方法是取巧的方法,先將文字讀取出來,轉換成列表,然後對每一行(字串形式)進行更改,最關鍵的字串的更改那裡,可以參考部落格:更改字串內容
上面這種方法的弊端就是,當檔案比較大的時候,會很麻煩,當然瞭如果更改內容比較少(你可以針對某些內容進行修改,讀取一部分內容,然後針對這一部分內容進行修改,然後在寫回去)
#第二種方法:插入文字到指定位置
import os
with open("Test.txt","r",encoding="utf-8") as file:
content = file.read()
content_add = "我加在這裡了!"
pos = content.find("周")
with open("Test.txt", "w", encoding="utf-8") as f:
if pos != -1:
content = content[:pos] +content_add+content[pos:]
f.write(content)
上面這種方法,比第一種方法本質上是一樣的,都是直接操作字串,但是一個是replace操作,一個是連線操作,感覺連線操作更好些一下
(5)讀取一個檔案的內容,然後寫入另一個檔案中
with open("Test.txt","r",encoding="utf-8") as file:
content = file.read()
with open("a.txt", "w", encoding="utf-8") as f:
f.write(content)
這個就比較簡單了,這裡不再詳細細說了。
5、二進位制檔案讀取
之前預設讀取的檔案都是文字檔案,想要讀取或者寫入二進位制檔案,只需要將“r”改成“rb”,其他模式也是這樣,只需加入一個b即可。
f = open('EDC.jpg', 'rb')
print(f.read())
# 輸出 '\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六進位制表示的位元組
大家可以看到,上面輸出的一個個字元編碼,簡單來說,任何非標準的文字檔案(對於Python2來說,標準是ASCLL,對於Python3來說,標準是Unicode),你需要用二進位制讀入這個檔案,然後再用.decode('...')的方法來解碼這個二進位制檔案:
f = open('DegangGuo.txt', 'rb')
# 讀入郭德綱老師的作文, 但是郭老師用的是參合著錯別字的繁體編碼,假設叫個"DeyunCode"
# 那麼你讀入以後,就需要解碼它
u = f.read().decode('DeyunCode')
建議大家還是看看原網頁,因為大部分原理性的東西,我沒有搞過來,只搞了些使用的東西,當然自己有添加了一些東西