讀寫文件是最常見的IO操作。Python內置了讀寫文件的函數,用法和C是兼容的。
讀寫文件前,我們先必須了解一下,在磁盤上讀寫文件的功能都是由操作系統提供的,現代操作系統不允許普通的程序直接操作磁盤,所以,讀寫文件就是請求操作系統打開一個文件對象(通常稱為文件描述符),然后,通過操作系統提供的接口從這個文件對象中讀取數據(讀文件),或者把數據寫入這個文件對象(寫文件)。
python 文件操作記住1個函數3個方法:(python中一切皆為對象)
open():請求打開文件:
如果文件存在返回文件的操作對象,
如果文件不存在,會拋出異常
關於open()的mode參數:
'r':讀
'w':寫
'a':追加
'r+' == r+w(可讀可寫,文件若不存在就報錯(IOError))
'w+' == w+r(可讀可寫,文件若不存在就創建)
'a+' ==a+r(可追加可寫,文件若不存在就創建)
對應的,如果是二進制文件,就都加一個b就好啦:
'rb' 'wb' 'ab' 'rb+' 'wb+' 'ab+'
read方法——讀取文件,將文件讀取到內存中,可以一次性讀取文件所有內容
write方法——將文件內容寫入到文件
close方法——關閉文件
准備文檔
往事隨風 - 齊秦
詞:許常德
曲:塗惠元
你的影子無所不在
人的心事像一顆塵埃
落在過去 飄向未來
掉進眼里就流出淚來
曾經滄海無限感慨
有時孤獨比擁抱實在
讓心春去 讓夢秋來
讓你離開
舍不得忘
一切都是為愛
沒有遺憾 還有我
就讓往事隨風 都隨風 都隨風 心隨你動
昨天花謝花開 不是夢 不是夢 不是夢
一、讀文件
1、打開文件
要以讀文件的模式打開一個文件對象,使用Python內置的open()
函數,傳入文件名和標示符.
首先,我們看下open函數的定義:
1 def open(file: Union[str, bytes, int], mode: str = ..., buffering: int = ..., encoding: Optional[str] = ..., errors: Optional[str] = ..., newline: Optional[str] = ..., closefd: bool = ...) Inferred type: (file: Union[str, bytes, int], mode: str, buffering: int, encoding: Optional[str], errors: Optional[str], newline: Optional[str], closefd: bool) -> IO Open file and return a stream. Raise IOError upon failure.
在一般情況下,我們打開文件只需要傳入文件路徑即可,這里我們讀取的文件的內容是中文,為了避免亂碼,我們在這里指定了編碼格式。其他的參數等我們用到的時候,在仔細說明。open函數返回一個表示文件的對象,python會將這個對象存儲在我們的變量f中,這樣我們就可以方便的操作了。
我先在桌面創建一個名叫test.txt的文本文件:
>>> f =open('C:\Users\24414\Desktop\test.txt','r',encoding='utf-8')//以絕對路徑打開文件 File "<stdin>", line 1 SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
出現這個錯誤的原因是:windows中的路徑是反斜杠\,然而反斜杠\在python中有着轉義字符的意義,所以在py文件中寫windows文件路徑的時候,要特別注意反斜杠\的使用。
下面有三種解決方式:
1 >>> f =open('C:\\Users\\24414\\Desktop\\test.txt','r',encoding='utf-8') 2 >>> 3 >>> f =open(r'C:\Users\24414\Desktop\test.txt','r',encoding='utf-8') 4 >>> 5 >>> f =open('C:/Users/24414/Desktop/test.txt','r',encoding='utf-8') 6 >>>
標示符'r'表示讀,這樣,我們就成功地打開了一個文件。
如果文件不存在,open()
函數就會拋出一個IOError
的錯誤,並且給出錯誤碼和詳細的信息告訴你文件不存在:
1 >>> f =open('C:\Users\24414\Desktop\test1.txt','r',encoding='utf-8') 2 File "<stdin>", line 1 3 SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape 4 >>>
2、讀取文件
2.1 讀取整個文件
操作文件的第一步就是得打開要操作的文件,然后進行讀取。在python中我們可以使用open函數來打開一個文件,然后使用read方法來讀取文件。
示例1,data.txt和我們的文件在同一目錄下,所以直接使用名稱即可::
如果是其他路徑(非程序目錄),那么直接使用絕對路徑:
我是在window的開發環境,以windows為例,我們在桌面有一個data1.txt文件:我們在將文件打開后,打印一下這個f到底有些什么:
>>> f = open(r'C:\Users\24414\Desktop\data1.txt', 'r',encoding='gbk') >>> f.read() '往事隨風 - 齊秦\n詞:許常德\n曲:塗惠元\n你的影子無所不在\n人的心事像一顆塵埃\n落在過去 飄向未來\n掉進眼里就流出淚來\n 曾經滄海無限感慨\n有時孤獨比擁抱實在\n讓心春去 讓夢秋來\n讓你離開\n舍不得忘\n一切都是為愛\n沒有遺憾 還有我\n就讓往事隨風 都隨風 都隨風 心隨你動\n昨天花謝花開 不是夢 不是夢 不是夢' >>>
print(dir(f))
輸出:
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
>>>
這些都是和文件操作相關的,在這里不會給大家做仔細說明,只是讓大家對這些東西有個印象,等遇到了,我們在說怎么使用。
2.2 逐行讀取
上面是讀取整個文件內容,接下來我們看下怎么逐行讀取文件。
在逐行讀取文本的時候,常見的可以使用for循環來讀取:
2.2.1 read()
read:如果指定了參數 size,就按照該指定長度從文件中讀取內容,否則,就讀取全文。被讀出來的內容,全部塞到一個字符串里面。這樣有好處,就是東西都到內存里面了,隨時取用;但如果文件內容太多了,內存會吃不消。注意換行符也為占用一個內存,缺點需要知道光標的位置,以及每一行的大小SIZE。
>>> f = open(r'C:\Users\24414\Desktop\data1.txt', 'r',encoding='gbk') >>> f.read(10) '往事隨風 - 齊秦\n' >>> f.read(5) '詞:許常德' >>>
2.2.2 readline()
概述:readline() 方法用於從文件讀取整行,包括 "\n" 字符。從字面意思可以看出,該方法每次讀出一行內容。所以,讀取時占用內存小,比較適合大文件,該方法返回一個字符串對象。如果指定了一個非負數的參數,則返回指定大小的字節數,包括 "\n" 字符。
語法
fileObject.readline();
參數
-
size -- 從文件中讀取的字節數。
返回值
返回從字符串中讀取的字節。
1 >>> f = open(r'C:\Users\24414\Desktop\data1.txt', 'r',encoding='gbk') 2 >>> while True: 3 ... line=f.read() 4 ... if not line:#到 EOF,返回空字符串,則終止循環 5 ... break 6 ... print(line) 7 ... 8 往事隨風 - 齊秦 9 詞:許常德 10 曲:塗惠元 11 你的影子無所不在 12 人的心事像一顆塵埃 13 落在過去 飄向未來 14 掉進眼里就流出淚來 15 曾經滄海無限感慨 16 有時孤獨比擁抱實在 17 讓心春去 讓夢秋來 18 讓你離開 19 舍不得忘 20 一切都是為愛 21 沒有遺憾 還有我 22 就讓往事隨風 都隨風 都隨風 心隨你動 23 昨天花謝花開 不是夢 不是夢 不是夢 24 >>>
2.2.3 readlines()
概述:readlines() 方法用於讀取所有行(直到結束符 EOF)並返回列表,該列表可以由 Python 的 for... in ... 結構進行處理。當文件太大時,內存可能不夠用。 如果碰到結束符 EOF 則返回空字符串。
如果碰到結束符 EOF 則返回空字符串。
語法
readlines() 方法語法如下:
fileObject.readlines( );
參數
-
無。
返回值
返回列表,包含所有的行。
1 >>> f = open(r'C:\Users\24414\Desktop\data1.txt', 'r',encoding='gbk') 2 >>> print(f.readlines()) 3 ['往事隨風 - 齊秦\n', '詞:許常德\n', '曲:塗惠元\n', '你的影子無所不在\n', '人的心事像一顆塵埃\n', '落在過去 飄向未來\n', '掉進眼里就流出淚來\n', '曾經滄海無限感慨\n', '有時孤獨比擁抱實在\n', '讓心春去 讓夢秋來\n', '讓你離開\n', '舍不得忘\n', '一切都是為愛\n', '沒有遺憾 還有我\n', '就讓往事隨風 都隨風 都隨風 心隨你動\n', '昨天花謝花開 不是夢 不是夢 不是夢'] 4 >>> for line in f.readlines(): 5 ... print(line) 6 ... 7 >>> 8 >>> 9 >>> f = open(r'C:\Users\24414\Desktop\data1.txt', 'r',encoding='gbk') 10 >>> for line in f.readlines(): 11 ... print(line) 12 ... 13 往事隨風 - 齊秦 14 15 詞:許常德 16 17 曲:塗惠元 18 19 你的影子無所不在 20 21 人的心事像一顆塵埃 22 23 落在過去 飄向未來 24 25 掉進眼里就流出淚來 26 27 曾經滄海無限感慨 28 29 有時孤獨比擁抱實在 30 31 讓心春去 讓夢秋來 32 33 讓你離開 34 35 舍不得忘 36 37 一切都是為愛 38 39 沒有遺憾 還有我 40 41 就讓往事隨風 都隨風 都隨風 心隨你動 42 43 昨天花謝花開 不是夢 不是夢 不是夢 44 >>>
2.3 讀取大文件
如果文件太大,就不能用 read() 或者 readlines() 一次性將全部內容讀入內存。
方法一、可以使用 while 循環和 readline() 來完成這個任務。
這里不再舉例
方法二、使用 fileinput 模塊:
1 >>> import fileinput 2 >>> for line in fileinput.input(r'C:\Users\24414\Desktop\data1.txt'): 3 ... print(line) 4 ... 5 往事隨風 - 齊秦 6 7 詞:許常德 8 9 曲:塗惠元 10 11 你的影子無所不在 12 13 人的心事像一顆塵埃 14 ......
第三種方法,直接讀取文件句柄,推薦使用這種方法
1 >>> f = open(r'C:\Users\24414\Desktop\data1.txt', 'r',encoding='gbk') 2 >>> f 3 <_io.TextIOWrapper name='C:\\Users\\24414\\Desktop\\data1.txt' mode='r' encoding='gbk'> 4 >>> for line in f: 5 ... print(line) 6 ... 7 往事隨風 - 齊秦 8 9 詞:許常德 10 11 曲:塗惠元 12 13 你的影子無所不在 14 15 人的心事像一顆塵埃
之所以能夠如此,是因為 file 是可迭代的數據類型,直接用 for 來迭代即可
3、 關閉文件
最后一步是調用
close()
方法關閉文件。文件使用完畢后必須關閉,因為文件對象會占用操作系統的資源,並且操作系統同一時間能打開的文件數量也是有限的:
1 f.close()
每次都要使用完都要關閉文件,很容易忘記,而且每次都這么寫實在太繁瑣,所以,Python引入了
with
語句來自動幫我們調用close()
方法:
1 >>> with open(r'C:\Users\24414\Desktop\data1.txt', 'r',encoding='gbk') as f: 2 ... for line in f: 3 ... print(line.strip()) 4 ... 5 往事隨風 - 齊秦 6 詞:許常德 7 曲:塗惠元 8 你的影子無所不在 9 人的心事像一顆塵埃 10 落在過去 飄向未來 11 掉進眼里就流出淚來 12 曾經滄海無限感慨
注意:
file-like Object
像open()
函數返回的這種有個read()
方法的對象,在Python中統稱為file-like Object。除了file外,還可以是內存的字節流,網絡流,自定義流等等。file-like Object不要求從特定類繼承,只要寫個read()
方法就行。
StringIO
就是在內存中創建的file-like Object,常用作臨時緩沖。
二進制文件
前面講的默認都是讀取文本文件,並且是UTF-8編碼的文本文件。要讀取二進制文件,比如圖片、視頻等等,用'rb'
模式打開文件即可:
字符編碼
要讀取非UTF-8編碼的文本文件,需要給open()
函數傳入encoding
參數,例如,讀取GBK編碼的文件:
1 >>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk') 2 >>> f.read() 3 '測試'
遇到有些編碼不規范的文件,你可能會遇到UnicodeDecodeError
,因為在文本文件中可能夾雜了一些非法編碼的字符。遇到這種情況,open()
函數還接收一個errors
參數,表示如果遇到編碼錯誤后如何處理。最簡單的方式是直接忽略:
1 >>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
二、寫文件
寫文件和讀文件是一樣的,唯一區別是調用open()
函數時,傳入標識符'w'
或者'wb'
表示寫文本文件或寫二進制文件
>>> f = open(r'C:\Users\24414\Desktop\data2.txt', 'w',encoding='gbk') >>> f.write('Hello world!') 12 >>>f.flush()
注意:如果指定路徑沒有data2.txt文件,它會先創建一個文件。
當我們寫文件時,操作系統往往不會立刻把數據寫入磁盤,而是放到內存緩存起來,空閑的時候再慢慢寫入。如果沒有這句代碼f.flush(),內容只是被寫到了緩沖區,等緩沖區滿了,才會將內容寫到文件中。有了這句代碼就會直接寫入到文本中,而不會等待緩沖區滿。當然也可以最后,調用f.close()實現相同的功能。沒有這一步的后果是數據可能只寫了一部分到磁盤,剩下的丟失了。
python文件對象提供了兩個“寫”方法: write() 和 writelines()。
- write()方法和read()、readline()方法對應,是將字符串寫入到文件中。
1 >>> with open(r'C:\Users\24414\Desktop\data2.txt','w',encoding='gbk') as f: 2 ... f.write('1\n2\n3\n') 3 ... 4 6 5 >>>
- writelines()方法和readlines()方法對應,也是針對列表的操作。它接收一個字符串列表作為參數,將他們寫入到文件中,換行符不會自動的加入,因此,需要顯式的加入換行符。
f1 = open(r'C:\Users\24414\Desktop\data2.txt'
, 'w')
f1.writelines(["1", "2", "3"])
# 此時test1.txt的內容為:123
f1 = open(
r'C:\Users\24414\Desktop\data2.txt', 'w')
f1.writelines(["1\n", "2\n", "3\n"])
# 此時test1.txt的內容為: # 1 # 2 # 3
三、file類的其他方法
3.1 tell()和seek() 注意:這里的位置指的是字節數,而不是字符個數
tell() | seek() | |
概述 | 返回文件的當前位置,即文件指針當前位置。 | 用於移動文件讀取指針到指定位置。 |
語法 | fileObject.tell() | fileObject.seek(offset[, whence]) |
參數 | 無 |
|
返回值 | 返回文件的當前位置。 | 如果操作成功,則返回新的文件位置,如果操作失敗,則函數返回 -1。 |
1 with open('data', 'r', encoding='utf-8') as f: 2 print(f.readline().rstrip()) 3 print(f.tell()) 4 print(len('往事隨風 - 齊秦\n'.encode('utf-8'))) 5 print(f.read(4)) 6 print(f.tell()) 7 f.seek(0) 8 print(f.readline().rstrip()) 9 輸出>>> 10 往事隨風 - 齊秦 11 23 12 22 13 詞:許常 14 35 15 往事隨風 - 齊秦
3.2 File flush()
概述 |
語法 |
參數 |
返回值 |
flush() 方法是用來刷新緩沖區的,即將緩沖區中的數據立刻寫入文件,同時清空緩沖區,不需要是被動的等待輸出緩沖區寫入。 一般情況下,文件關閉后會自動刷新緩沖區,但有時你需要在關閉前刷新它,這時就可以使用 flush() 方法。 |
fileObject.flush(); |
無 | 該方法沒有返回值。 |
1 import sys,time 2 for i in range(50): 3 sys.stdout.write('#') 4 sys.stdout.flush() 5 time.sleep(0.5) 6 輸出>>> 7 ##################################################
3.3 next()
概述 | 語法 | 參數 | 返回值 |
Python 3 的內置函數 next() 通過迭代器調用 __next__() 方法返回下一項。 在循環中,next()方法會在每次循環中調用,該方法返回文件的下一行,如果到達結尾(EOF),則觸發 StopIteration | next(iterator[,default]) |
無 | 返回文件下一行。 |
文檔內容:
第一行
第二行
第三行
第四行
第五行
示例:
1 >>> f=open(r'C:\Users\24414\Desktop\data1.txt','r') 2 >>> for index in range(5): 3 ... line=next(f) 4 ... print(index,':',line) 5 ... 6 0 : 第一行 7 8 1 : 第二行 9 10 2 : 第三行 11 12 3 : 第四行 13 14 4 : 第五行 15 >>>
3.4 truncate()
概述 |
語法 |
返回值 |
參數 |
truncate() 方法用於從文件的首行首字節開始截斷,截斷文件為 size 個字節,無 size 表示從當前位置截斷;截斷之后 V 后面的所有字節被刪除. | fileObject.truncate( [ size ]) |
該方法沒有返回值。 | size -- 可選,如果存在則文件截斷為 size 字節。 |
有坑:根據上述文檔說明不帶參truncate()函數會截取文件頭到當前游標位置的字節。
但是,當文件以文本的形式打開時,進行讀操作造成游標改變時,文件不會被截取.同樣的操作,當文件以二進制方式打開時,文件才會被截取.原因還未知
1 f = open("data1", "r+", encoding="utf-8") 2 print(f.readline()) 3 f.truncate() 4 f.seek(0) 5 print(f.read()) 6 輸出>>> 7 第一行 8 9 第一行 10 第二行 11 第三行 12 第四行 13 第五行
1 f = open("data1", "r+", encoding="utf-8") 2 print(f.readline()) 3 f.truncate(10) 4 f.seek(0) 5 print(f.read()) 6 輸出>>> 7 第一行 8 9 第一行
3.5 不常用的兩個操作
fileno() | 返回一個整型的文件描述符,可以用於一些底層IO操作上(如,os模塊的read方法) |
isatty() | 判斷文件是否被連接到一個虛擬終端,是則返回True,否則返回False |