Python中文件操作
- 2019 年 10 月 27 日
- 筆記
1 文件操作
無論在那種語言中都會對文件進行操作處理,而文件相關的處理無非就是打開文件,讀取或者寫入內容,最後再是關閉文件。ython中文件常用的IO操作有以下幾個:
Function | Operation |
open | 打開 |
read | 讀取 |
write | 寫入 |
close | 關閉 |
readline | 行讀取 |
readlines | 多行讀取 |
seek | 文件指針操作 |
tell | 指針位置 |
2 打開操作
文件的打開操作是對文件進行操作的第一步,Python中提供open函數,open(file, mode=’r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None),open 函數是打開一個文件,返回以文件對象(l流對象)和文件描述符。打開文件失敗,則返回異常。
2.1 open函數基本使用
創建一個文件test,然後打開它,用完關閉。

1 f = open("test") # file對象 2 # windows <_io.TextIOWrapper name='test' mode='r' encoding='cp936'> 3 # linux <_io.TextIOWrapper name='test' mode='r' encoding='UTF-8'> 4 print(f.read()) # 讀取文件 5 f.close() # 關閉文件
View Code
文件操作找那個,最常用的操作就是讀和寫。文件訪問的模式有兩種:文本模式和二進制模式。不同模式下,操作函數不盡相同,表現的結果也不一樣。
2.2 open函數的參數
2.2.1 file
open函數中的file文件是指定要打開或者要創建的文件名,如果不指定路徑,默認是當前路徑。
2.2.3 mode模式
open函數中提供mode參數,可以控制是以什麼方式打開文件的,不同的模式適用不同場景下對文件的操作。以下是打開文件的模式以及其對應的作用:
Description | Meaning |
r | 缺省的,表示只讀打開 |
w | 只寫打開 |
x | 創建並寫入一個新文件 |
a | 寫入打開,如果文件存在,則追加 |
b | 二進制模式 |
t | 缺省的,文本模式 |
+ | 讀寫打開一個文件。給原來只讀、只寫方式打開提供缺失的讀或者寫能力 |
在2.1的例子中,打開test文件時沒有指定mode參數,那麼就是默認以文本打開模式並且是以只讀模式打開test文件。
- r模式
open默認是只讀模式r打開已經存在的文件,如果文件不存在,拋出FileNotFoundErro異常。只讀打開文件,如果使用write方法,會拋出異常。
- w模式
如果mode後是w描述符,表示只寫方式打開文件,如果讀取則拋出異常。如果文件不存在,則直接創建文件,如果文件存在,則清空文件內容。
- x模式
x模式下如果文件不存在,創建文件,並以只寫方式打開文件;文件存在時,拋出FileExistsError異常。
- a模式
a模式下,文件存在,只寫模式打開,追加內容;文件不存在,則創建後,只寫模式打開文件,追加內容。
- 文本模式t
字符流,將文件的位元組按照某種字符編碼理解,按照字符操作。open的默認mode是rt。
- 二進制模式b
位元組流,將文本按照位元組理解,與字符編碼無關。二進制模式操作是,位元組操作使用bytes類型。
- +模式
為r、w、a、x提供確實的讀寫功能。但是,獲取文件對象依舊按照r、w、a、x。+不能單獨使用,可以認為它是為前面的模式字符做增強功能的。
2.2.4 buffering: 緩衝區
Python在處理文件時,是在內存中對文件進行處理。當給文件中寫入內容是,不是立即將內容寫入磁盤中,而是先寫入內存中,存入緩衝區中待緩衝區滿了或者在關閉文件之前,將內存中的內容寫入磁盤中。
緩衝區是一個內存空間,一般來說是一個FIFO隊列,到緩衝區滿了或者達到閾值時數據才會flush到磁盤。flush函數是將緩衝區數據寫入磁盤,close()關閉前會調用flush函數。io.DEFAULT_BUFFER_SIZE缺省緩衝區大小,單位是位元組,默認是4096或者8192。
open函數中的buffering參數,用-1表示使用缺省大小的buffer。如果是二進制模式,使用io.DEFAULT_BUFFER_SIZE值;如果是文本模式,如果是是終端設備,是行緩方式,如果不是則使用二進制模式的策略。
- 0隻在二進制模式使用,表示關buffer
- 1隻在文本模式使用,表示使用行緩衝。意思就是見到換行符就flush
- 大於1用於指定buffer的大小
1、二進制下的例子

1 import io 2 3 f = open('test4', 'w+b') 4 print(io.DEFAULT_BUFFER_SIZE) 5 f.write('baidu.com'.encode()) 6 # cat test4 7 f.seek(0) 8 # cat test4 9 f.write('www.baidu.com'.encode()) 10 f.flush() 11 f.close() 12 13 f = open('test4', 'w+b', 4) 14 f.write(b'dab') 15 # cat test4 16 f.write(b'ric') 17 # cat test4 18 f.close
View Code
2、文本模式下

1 # buffering=1,使用行緩衝 2 f = open('test4', 'w+', 1) 3 f.write('dab') # cat test4 4 f.write('dabric'*4) # cat test4 5 f.write('n') # cat test4 6 f.write('HellonPython') # cat test4 7 f.close 8 9 # buffering>1,使用指定大小的緩衝區 10 f = open('test4', 'w+', 15) 11 f.write('dab') # cat test4 12 f.write('ric') # cat test4 13 f.write('Hellon') # cat test4 14 f.write('nPython') # cat test4 15 f.write('a' * (io.DEFAULT_BUFFER_SIZE - 20)) # 設置為大於1沒什麼用 16 f.write('nwww.baidu.com/python') 17 f.close
View Code
buffering=0,這時一種特殊的二進制模式,不需要內存的buffer,可以看做是一個FIFO的文件。

1 f = open('test4', 'wb+', 0) 2 f.write(b'd') # cat test4 3 f.write(b'a') # cat test4 4 f.write(b'b') # cat test4 5 f.write(b'dabric'*4) # cat test4 6 f.write(b'n') # cat test4 7 f.write(b'HellonPython') # cat test4 8 f.close
View Code
buffering為不同值下多代表的含義總結如下表:
buffering | Introduction |
buffering=-1 | t和b都是io.DEFAULT_BUFFER_SIZE |
buffering=0 |
b關閉緩衝區 t不支持 |
buffering=1 |
b就1個位元組 t行緩衝,遇到換行讀才flush |
buffering>1 |
b模式表示行緩衝大小。緩衝區的值可以超過io.DEFAULT_BUFFER_SIZE, 直到設定的值超出後才把緩衝區flush t模式,是io.DEFAULT_BUFFER_SIZE,flush完後把當前字符串也寫入磁盤 |
似乎看起來很瑪法,一般來說,只需記得以下幾點:
- 文本模式,一般都使用默認緩衝區大小
- 二進制模式,是一個個位元組的操作,可以指定buffer的大小
- 一般來說,默認緩衝區大小是個比較好的選擇,除非明確知道,否則不調整它
- 一般編程中,明確知道需要寫磁盤了,都會手動調用一次flush,而不是等到自定flush或者close的時候
2.2.5 encoding: 編碼,僅文本模式使用
文件在磁盤中的存儲是以位元組形式存儲的,如果要講文件顯示在屏幕上的話,就要對其進行解碼,反過來寫入時就要對其進行編碼。Python中的open函數會在讀取文件時做必要的解碼,以文本模式寫入文件時還會做必要的編碼,所以在對文件調用讀取或者是寫入操作都是字符串對象。
encoding參數指定編碼的格式,None標識使用缺省編碼,依賴操作系統。windows下缺省GBK,Linux下缺省UTF-8。在對文件的讀取和寫入操作時要保證編碼格式的一致,如果使用默認的編碼格式的話,在不同的操作系統中可能會產生亂碼的現象,如下:

1 >>> open('cafe.txt', 'w', encoding='utf_8').write('café') 2 4 3 >>> open('cafe.txt').read() 4 'café'
View Code
寫入時文件指定UTF-8編碼,但是讀取文件時沒有那麼做。有可能在對文件進行讀取時是在windows環境下,那麼windows環境下使用的默認編碼集為GBK,於是就會產生亂碼現在。解決辦法是在讀取操作之前,打開文件制定編碼格式為UTF-8即可。
2.2.6 其他參數
- errors
errors參數標識什麼樣的編碼錯誤將被捕獲。一般情況下,None和strict表示有編碼錯誤將拋出ValueError異常;ingore標識忽略編碼錯誤。
- newline
newline參數表示在文本模式中的換行的轉換。可以為None、”空串、”r”、”n”、”rn”。
讀時,None表示”r”、”n”、”rn”都被轉換為’n’;”表示不會自動轉換通用換行符;其它合法字符表示換行符就是制定字符,就會按照制定字符分行。
寫時,None表示’n’都會被替換為系統缺省分隔符os,linesep;’n’或”表示’n’不替換;其它合法字符表示’n’會被替換為指定的字符。

1 f = open('o:/test', 'w') 2 f.write('pythonrwww.python.orgnwww.baidu.comrnpython3') 3 f.close() 4 5 newlines = [None, '', 'n', 'rn'] 6 for nl in newlines: 7 f = open('o:/test', 'r+', newline=nl) # q缺省替換所有換行符 8 print(f.readlines()) 9 f.close
View Code
- closefd
關閉文件描述符,True表示關閉它。False會在文件關閉後保持這個描述符。fileobj.fileno()查看。
3 讀取操作
3.1 read函數
read函數讀取文件時,將整個文件中的內容讀取到內存中,對於小文件的讀取可以使用read,其不適用大文件的讀取。read函數中的size參數表示讀取的多少個字符或位元組;負數或None表述讀取到EOF。

1 f = open('o:/test4', 'r+', 0) 2 f.write("dabric") 3 f.write('n') 4 f.write('你好') 5 f.seek(0) 6 f.read(7) 7 f.close 8 9 # 二進制 10 f = open('test4', 'rb+') 11 f.read(7) 12 f.read(1) 13 f.close()
View Code
3.2 行讀取
readline函數表示一行行讀取文件內容。size設置一個能讀取行內幾個字符或位元組。
readlines函數表述讀取所有行的列表。指定hint則返回指定的行數。

1 # 按行迭代 2 f = open('test') # 返回可迭代對象 3 4 for line in f: 5 print(line) 6 7 f.close()
View Code
4 寫入操作
寫入操作在前面的代碼中多多少少都使用過,下面具體看下寫入操作的函數。
write(s),函數把字符串s寫入到文件中並返回字符的個數;
writelines(lines),將字符串列表寫入文件。

1 f = open('test', 'w+') 2 3 lines = ['abc', '123n', 'dabric'] # 提供換行符 4 f.writelines(lines) 5 6 f.seek(0) 7 print(f.read()) 8 f.close()
View Code
5 文件指針
文件是通過文件指針來記錄文件當前指向的位元組位置,我們可以通過控制文件指針,來指向指定的位元組位置。在模式為r的情況下,文件指針指向起始0位置,表示文件開頭;在模式為a的情況下,文件指針指向EOF,表示文件末尾,所以a模式被稱為追加模式。Python中提供tell函數來顯示文件指針當前的位置。
5.1 文件指針操作
Python提供seek(offset[, whence])函數來系統文件指針位置。其中offset表示變異多少位元組,whence表示從哪裡開始。
在文本模式下,whence的取值:
- whence 0 缺省值,表示從文件頭開始,offset只能為正整數
- whence 1 表示從當前位置,offset只能接受0
- whence 2 表示從EOF開始,offset只能接受0

1 # 文本模式 2 f = open('test4', 'r+') 3 f.tell() # 起始 4 f.read() 5 f.tell() # EOF 6 f.seek(0) # 起始 7 f.read() 8 f.seek(2, 0) 9 f.read() 10 f.seek(2, 0) 11 f.seek(2, 1) # offset必須為0 12 f.seek(2, 2) # offset必須為0 13 f.close()
View Code
文本模式支持從開頭向後偏移的方式。whence為1表示從當前位置開始偏移,但是只支持偏移0,相當於原地不動,所以沒有什麼用;whence為2表示從EOF開始,只支持偏移0,相當於移動文件指針到EOF。seek函數時按照位元組偏移的。
在二進制模式下,whence的取值:
- whence 0 缺省值,表示從文件頭開始,offset只能為正整數
- whence 1 表示從當前位置,offset可正可負
- whence 2 表示從EOF開始,offset可正可負

1 # 二進制模式 2 f = open('test4', 'rb+') 3 f.tell() # 起始 4 f.read() 5 f.tell() # EOF 6 f.write(b'abc') 7 f.seek(0) # 起始 8 f.seek(2, 1) # 從當前指針開始,向後偏移2個位元組 9 f.read() 10 f.seek(-2, 1) # 從當前指針開始,向前偏移2個位元組 11 12 f.seek(2, 2) # 從EOF開始,向後便宜2個位元組 13 f.seek(0) 14 f.seek(-2, 2) # 從EOF開始,向前偏移2個位元組 15 f.read() 16 17 f.seek(-20, 2) # OSError 18 f.close()
View Code
二進制模式下支持任意起點的偏移,從頭、從尾、從中間位置開始的偏移。向後seek可以超界,但是向前seek的時候,不能超界,否則拋出異常。
6 上下文管理
在每次打開使用文件結束後,都要使用close函數關閉文件對象,有時候在編程時難免會忘記使用close函數對打開的文件關閉,這樣會導致該文件在其他地方不能使用的可能。Python提供一種上下文管理機制,使用它後在文件使用完後會自動關閉文件對象。
使用with…as關鍵字,在with語句執行完的時候,會自動關閉文件對象。注意,上下文管理的語句塊並不會開啟新的作用域。

1 with open('test') as f: 2 f.write("abc") # 文件只讀,寫入失敗 3 4 # 測試f是否關閉 5 f.close # f的作用域
View Code
另一種寫法;

1 f1 = open('test') 2 with f1: 3 f1.write('abc') # 文件只讀,寫入失敗 4 5 # 測試f是否關閉 6 f1.close # f1的作用域
View Code
對於類似於文件對象的IO對象,一般來說都性需要在不使用的時候關閉、註銷,以釋放資源。IO被打開的時候,會獲得一個文件描述符。計算資源是有限的,所以操作系統都會做限制。就是為了保護計算機的資源不要被完全耗盡,計算資源是共享的,不是獨佔的。一般情況下,除非特別明確的直到資源情況,否則不要提高資源的限制值來解決問題。