前言
文件讀寫可以說是最常用的功能之一,總結一下python內置的讀寫文件的方法。
打開文件
- open
def open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True):
pass
參數說明:
-
file:文件路徑,可以是相對路徑,也可以是絕對路徑
-
mode: 文件的讀寫方式,默認‘r’,只讀方式;
-
buffering:設置緩沖策略,0用於二進制文件,1為行緩沖,用於文本模式;默認二進制文件固定大小緩沖,文本文件行緩沖
-
encoding:設置編碼,默認utf-8;該參數不能用於二進制模式;
-
errors:設置怎么處理文件的編碼異常,默認strict,發生錯誤拋出異常;設置ignore忽略編碼異常,可能導致數據丟失,這個參數不能用於二進制模式;
-
newline:設置換行符,默認換行符為'\n','\r','\r\n',寫入文件的時候,所有文本中的上述三種都會轉換成'\n'換行符;當設置為''時,也啟用默認模式;如果設置其它合法值,則使用其他值,一般不用;
-
closefd=True :設置文件描述符的狀態,當為False時,文件關閉但描述符不關閉,但是打開文件時指定了文件名,那么設置False不會起作用。
文件打開方式介紹
r: 以只讀方式打開文件。文件的指針將會放在文件的開頭。這是默認模式。
w: 打開一個文件只用於寫入。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
a: 打開一個文件用於追加。如果該文件已存在,文件指針將會放在文件的結尾。也就是說,新的內容將會被寫入到已有內容之后。如果該文件不存在,創建新文件進行寫入。
x: 創建一個新文件,將其打開並編寫;如果文件已經存在報錯;
rb: 以二進制格式打開一個文件用於只讀。文件指針將會放在文件的開頭。這是默認模式。
wb: 以二進制格式打開一個文件只用於寫入。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
ab: 以二進制格式打開一個文件用於追加。如果該文件已存在,文件指針將會放在文件的結尾。也就是說,新的內容將會被寫入到已有內容之后。如果該文件不存在,創建新文件進行寫入。
r+: 打開一個文件用於讀寫。文件指針將會放在文件的開頭。
w+: 打開一個文件用於讀寫。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
a+: 打開一個文件用於讀寫。如果該文件已存在,文件指針將會放在文件的結尾。文件打開時會是追加模式。如果該文件不存在,創建新文件用於讀寫。
rb+:以二進制格式打開一個文件用於讀寫。文件指針將會放在文件的開頭。
wb+:以二進制格式打開一個文件用於讀寫。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
ab+:以二進制格式打開一個文件用於追加。如果該文件已存在,文件指針將會放在文件的結尾。如果該文件不存在,創建新文件用於讀寫。
關閉文件
- close()
f = open(file)
f.close()
注意:
由於文件每次打開都需要調用Close函數關閉,可以使用上下文管理器管理文件,其會自動在適當的時候關閉文件。
with open(filename) as f:
f.read()
文件普通讀寫
讀
方法一:read()
with open(filename) as f:
f.read() # 一次性讀取全部,在大文件這是不可取的
f.read(5) # 讀取5個字節,如果使用UTF-8編碼,5表示5個漢字,也表示5個字母。
f.read(6) # 同一個f對象,多次讀取時在上一次的基礎上繼續往下,如果超過則有多少讀多少
方法二:readline()
with open(filename) as f:
f.readline() # 按行來讀取,每次返回一行,
f.readline() # 多次讀取在上一行的基礎上往下,換行符也會被讀取
方法三:readlines()
with open(filename) as f:
t = f.readlines() # 按行讀取全部的內容作為一個列表返回
方法四:直接遍歷打開的文件對象,按行讀取
with open('./test.txt','r+') as f:
for line in f:
print(line)
方法五:使用linecache模塊
import linecache
line = linecache.getline(filename,5) # 讀取文件第五行的數據
說明:getline方法主要用來獲取特定的行的內容,當多次讀取文件不規則行的時候該方法能提高效率,因為其將文件的內容緩存了一份在內存中,不用每次去磁盤讀取,減少了IO,但這也意味着當讀取很大的文件的時候,內存消耗十分嚴重,因為getline內部使用了readlines方法;大文件可能造成內存溢出。
- readable()方法可以用來判斷文件是否可讀
寫
方法一:write()
with open(filename,'w',encoding='utf-8') as f:
t = f.write('ddd') # 將內容寫入,必須是字符串格式,不可以是數字,返回的是字符串的個數,包括了換行符\r\n占兩個字符(windows),占一個字符\n(linux)。
t = f.write('aaa') # 多次寫入在原來的基礎上繼續寫入
方法二:writelines()
with open(filename,'w',encoding='utf-8') as f:
f.writelines(['aa','bb']) # 該方法自動將列表元素拼接起來寫入文件,參數時一個可迭代對象,列表、字典、集合都可以
- writeable()可以判斷打開的文件對象f究竟是可讀的還是可寫的。
注意的問題
-
f.write(123):這種是不被允許的,必須轉化成二進制或字符串;
-
open(filename,'rb',encoding='utf-8'):以二進制格式打開文件是不能指定編碼的,否則報錯
-
以二進制格式打開文件,寫入的必須是字節文件,同時寫入返回的是字節數;以文本模式打開,寫入的必須是文本,同時返回的是字符串個數;一個漢字字符串占3個字節。
普通字符串轉化為字節文件的方法:
-
b'123adf':這種方法只能針對數字和字母,默認使用ascii編碼,不能轉化漢字;
-
'123adf'.encode('utf-8):可以轉化字母和漢字;
-
bytes('我的',encoding='utf-8'):可以轉化字母和漢字,但必須指定編碼格式;
文件的定位讀寫
- tell():獲取文件指針的位置
with open(filename,'r',encoding='utf-8') as f:
f.read(3)
t = f.tell() # 獲取指針的位置,返回3,如果讀取到換行符,換行符在windows占兩個字節
- seek(offset,from):設置指針的位置
參數
- offset:偏移量,設置負數表示向前偏移,正數表示向后偏移,一個漢字占3個字節,字母占一個字節
- from:偏移的方向
0:表示文件開頭
1:表示當前位置
2:表示文件末尾
with open(filename,'r',encoding='utf-8') as f:
f.read(3)
a = f.seek(0,0) # 返回指針定位后的實際位置,默認偏移方向從文件開頭算起
注意
-
在文本模式下打開文件,偏移方向只能從文件開頭算起,參數from只能為0,這是因為文本模式涉及到編碼的問題,以二進制格式打開文件就可以設置不同的方向。
-
每次打開文件,讀操作的指針都是在文件的開頭,寫操作的指針在文件的末尾,直到文件被關閉;
幾個經典的案例
文件同時讀寫的情況
with open('test.txt', 'r+') as f:
print(f.tell()) # 當前指針的位置在開頭0
a = f.read(3) # 指針到了位置3
print(a)
print(f.tell())
h = f.write('hhh') # 從文件的末尾寫入,相當於追加數據
print(h)
c = f.tell() # 指針在文件的末尾
print(c)
print(f.read()) # 讀取從位置3到文件原來的末尾的數據
print(f.tell()) # 當前位置在新的文件末尾和c相同
- 總結:r+模式下,如果同一個文件對象需要讀和寫,讀和寫各有一個指針,它們是相互獨立的,讀寫位置各不干擾;但是tell函數獲取到的位置優先表示寫的位置;即當程序執行時,如果只有讀,tell獲取的位置是讀的位置;如果出現了寫,tell獲取的位置就一定是寫的位置了,無論后面還有沒有更多的讀操作;同理其他的模式也一樣;
文件同時讀寫存在偏移的情況
with open('test.txt', 'r+') as f:
print(f.tell()) # 當前讀指針的位置在開頭0,寫指針還未初始化
print(f.read(3)) # 讀指針到了位置3
print(f.tell()) # 獲取到讀指針的位置為3
f.seek(0) # 指針偏移到文件的開頭,只能影響一個指針
print(f.read(3)) # 讀指針到了位置3
h = f.write('gggg') # 增加一個文件描述符,寫指針初始化到文件的末尾,寫入數據
j = f.tell() # 獲取寫的位置優先,寫指針在文件末尾
print(j)
f.seek(0) # 指針偏移到文件的開頭,讀文件的數據更新,加入了寫入的數據
j = f.tell() # 獲取寫指針在文件的開頭
print(j)
h = f.write('vvvv') # 從文件開頭寫入數據,覆蓋原來的數據
c = f.tell() # 獲取寫指針在文件的位置4
print(c)
print(f.read(4)) # 讀取從位置4往后的4個字符
print(f.tell()) # 讀指針在文件8位置
h = f.write('xxx') # 從文件的末尾寫入,相當於追加數據
print(f.tell()) # 當前位置在新的文件末尾
print(f.read()) # 從8位置讀取所有的數據
- 總結:
-
seek()函數只能影響它下面的第一次的讀寫操作;並且會將上一次寫入的數據更新到讀緩沖區中;
-
在r+模式下,write()總是從文件的末尾寫入,除非受seek函數的影響;
-
write()操作被偏移影響后,其后的read操作的指針會被移到write操作的位置;
文件同時讀寫存在且存在更新的情況
with open('test.txt', 'r+') as f:
f.read()
f.seek(2,0) # 定位到到文件的開頭往右2的位置
f.truncate() # 將2位置后所有的數據刪除
f.write('aaa') # 寫入新的數據
- truncate():動態刪除數據,從當前指針刪除后面所有的數據
其他方法
-
flush():刷新文件內部緩沖,直接把內部緩沖區的數據立刻寫入文件, 而不是被動的等待輸出緩沖區寫入,一般close()函數時先調用flush(),然后再關閉文件描述符。
-
fileno:返回一個整型的文件描述符,很少用到;
-
isatty():文件是否連接到一個終端的設備;
-
truncate():文件截取,無參數時從當前的指針位置刪除后面所有;有參數時從首行首字母截取數據留下來,其余的刪除;