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