Python中檔案操作
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.closeView 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('Hello\nPython') # 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('Hello\n') # 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.closeView 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'Hello\nPython') # cat test4 8 f.closeView 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"、"\r\n"。
讀時,None表示"\r"、"\n"、"\r\n"都被轉換為'\n';''表示不會自動轉換通用換行符;其它合法字元表示換行符就是制定字元,就會按照制定字元分行。
寫時,None表示'\n'都會被替換為系統預設分隔符os,linesep;'\n'或''表示'\n'不替換;其它合法字元表示'\n'會被替換為指定的字元。
1 f = open('o:/test', 'w') 2 f.write('python\rwww.python.org\nwww.baidu.com\r\npython3') 3 f.close() 4 5 newlines = [None, '', '\n', '\r\n'] 6 for nl in newlines: 7 f = open('o:/test', 'r+', newline=nl) # q預設替換所有換行符 8 print(f.readlines()) 9 f.closeView 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', '123\n', '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被開啟的時候,會獲得一個檔案描述符。計算資源是有限的,所以作業系統都會做限制。就是為了保護計算機的資源不要被完全耗盡,計算資源是共享的,不是獨佔的。一般情況下,除非特別明確的直到資源情況,否則不要提高資源的限制值來解決問題。