1. 程式人生 > >Python中檔案操作

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.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('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.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'Hello\nPython')    # 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"、"\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.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', '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被開啟的時候,會獲得一個檔案描述符。計算資源是有限的,所以作業系統都會做限制。就是為了保護計算機的資源不要被完全耗盡,計算資源是共享的,不是獨佔的。一般情況下,除非特別明確的直到資源情況,否則不要提高資源的限制值來解決問題。