Python I/O編程 --讀寫文件、StringIO/ BytesIO


I/O編程

Input/Output  輸入/輸出

Stream(流)是一個很重要的概念,可以把流想象成一個水管,數據就是水管里的水

Input Stream就是數據從外面(磁盤、網絡)流進內存,Output Stream就是數據從內存流到外面去

    由於CPU和內存的速度遠遠高於外設的速度,所以,在I/O編程中,存在速度嚴重不匹配的問題。例子:比如要把100M的數據寫入磁盤(這是output),CPU輸出100M只需要0.01s,可是磁盤要接收這100M數據可能需要10s,怎么辦呢?有兩種解決方法:

第一種:CPU等着,這種模式稱為同步IO

第二種:CPU不等着,這種模式稱為異步IO

同步和異步的區別:在於是否等待IO執行的結果.

例子:好比你去麥當勞點餐,你說“來個漢堡”,服務員告訴你,對不起,漢堡要現做,需要等待5分鍾, 有兩種處理方式:

(1)你站在收銀台前等了5分鍾,拿到漢堡再去逛商場,這是同步IO

(2)你先逛商場,等做好了,服務員再通知你,這樣你可以立刻去干別的事情(逛商場),這是異步IO

     結論:很明顯,使用異步IO來編寫程序性能會遠遠高於同步IO,但是異步IO的缺點是編程模型復雜。而服務員如何通知你漢堡做好了,方法各不相同。

   (1) 如果是服務員跑過來找你,這是回調模式

   (2)如果服務員發短信通知你,你就要不停地檢查手機,這是輪詢模式

    總之,異步IO的復雜度遠遠高於同步IO

   操作IO的能力都是由操作系統提供的,每一種編程語言都會把操作系統提供的低級C接口封裝起來方便使用,Python也不例外

 

文件讀寫

讀文件(read)和寫文件(write)

注意:由於“ \ ”是字符串中的轉義符,所以表示路徑時,使用“ \\ ”或者 “ / ”或者“ \\ ”

 

Python對文本文件和二進制文件采用統一的操作步驟,即“ 打開 - 操作 - 關閉”

讀寫文件的模式說明:

訪問模式 說明
r 默認模式,以只讀方式打開文件,文件的指針將會放在文件的開頭。如果文件不存在,返回異常FileNotFoundError
w 打開一個文件只用於寫入。如果文件已存在,則將其覆蓋。如果文件不存在,創建新文件
a 打開一個文件用於追加。如果該文件已存在,文件指針將會放在文件的結尾。也即是,新的內容會被追加到已有內容之后。如果文件不存在,創建新文件進行寫入。
x 創建寫模式,文件不存在則創建,存在則返回異常FileExistsError
t 文本文件模式,默認值
rb 以二進制格式打開一個文件用於只讀。文件指針將會放在文件的開頭。
wb 以二進制格式打開,……(其余的與相應模式的用法相同)
ab 以二進制格式打開,……(其余的與相應模式的用法相同)
+ 與r/w/x/a/rb/wb/ab一同使用,在原功能基礎上,使其同時具有讀寫功能
b 二進制文件模式

注意:(追加append)

 

open( )函數:打開一個文件對象

    格式:open( 文件名,文件模式)

文件模式:“r”模式表示讀取utf-8編碼的文本文件; “rb”模式表示讀取二進制文件

文件模式:“w”模式表示寫文本文件;“wb”模式表示寫二進制文件

 二進制文件:圖片、視頻、聲音

 

字符編碼:

讀取非utf-8編碼的文本文件,使用encoding參數

文本文件夾雜一些非法編碼的字符時,open()函數使用errors參數,表示如果遇到編碼錯誤后如何處理,最簡單的方式是直接忽略。

f = open('/Users/michael/notfound.txt', 'rb',encoding='gbk',errors='ignore')

 

讀文件——文件存在

 如果文件成功打開,讀取文件的方式:

read( )方法:一次讀取文件的全部內容,Python把內容讀到內存,用一個str對象表示;如果文件有10G,內存就爆了,所以保險起見,可以反復調用read(size)方法。

read(size)方法:每次最多讀取size個字節的內容。

readline( ):每次讀取一行內容

readlines( ):一次讀取所有內容並按行返回一個列表list,列表中的每一個元素為文件中的每一行數據。同時,每一個元素結尾都帶一個 \n標志。

  readlines(h):參數可選,如果給出,讀入h行

 

讀文件——文件不存在

 如果文件不存在,ope( )函數會拋出一個IOError錯誤,並且給出錯誤碼和詳細的信息告訴你文件不存在:

1 f = open('/Users/michael/notfound.txt', 'r')

運行結果:

 寫文件

f.write( s ):向文件寫入一個字符串或字節流。     要顯示地使用 '  \n '對寫入文本進行分行,如果不進行分行,每次寫入的字符串會被連接起來。

 

f.writelines( lines ):將一個元素為字符串的列表整體寫入文件。  直接將列表類型的各元素連接起來寫入文件f。

 

當我們寫文件時,操作系統往往不會立刻把數據寫入磁盤,而是放到內存緩存起來,空閑的時候再慢慢寫入。

只有調用close( )方法時,操作系統才保證把沒有寫入的數據全部寫入磁盤。

忘記調用close( )的后果是數據可能只寫了一部分到磁盤,剩下的丟失了。所以,還是用with語句來得保險:

with open('/Users/michael/test.txt', 'w') as f: f.write('Hello, world!')

 

 

close( )方法

    文件使用完畢后必須關閉,有兩方面原因:

(1)文件對象會占用操作系統的資源。  (2)操作系統同一時間能打開的文件數量也是有限的。

 

為了保證無論是否出錯都能正確地關閉文件,有兩種實現方式:

(1)使用try ... finally來實現,因為finally語句塊一定會被執行。

(2)with語句:是因為with語句能自動調用close( )方法

使用try ... finally來實現:

 try: f = open('/path/to/file', 'r') print(f.read()) finally: if f: f.close()

 with語句:

with open('/path/to/file', 'r') as f: print(f.read())

這和前面的try ... finally是一樣的,但是代碼更佳簡潔,並且不必調用f.close()方法。

 

 

文件的定位讀寫

  背景:在實際開發中,可能會需要從文件的某個特定位置開始讀寫。此時,需要對文件的讀寫位置進行定位。

兩種定位方式:

(1)獲取文件當前的讀寫位置:tell( )方法

(2)定位到文件的指定讀寫位置:seek (offset, whence )方法 

 

1、使用tell方法來獲取文件當前的讀寫位置

tell方法返回文件的當前位置,即文件指針當前位置

f=open("theima.txt","r") str=f.read(4) print("讀取的數據是:",str) #查找當前位置
position=f.tell() print("當前文件位置:",position)

 

2、使用seek方法定位到文件的指定讀寫位置

格式: seek( offset [ , whence ] )

offset:偏移量,即需要移動的字節數

whence表示方向,有三個值:

(1)SEEK_SET或者0:默認值,表示從文件的起始位置開始偏移

(2)SEEK_CUR或者1:表示從文件的當前位置開始偏移

(3)SEEK_END或者2:表示從文件末尾開始偏移

 

  io.UnsupportedOperation: can't do nonzero cur-relative seeks錯誤

 實例:

f=open("E:/test/憫農.txt",'r')
str=f.read(17)
print("讀取的數據是:",str)
position=f.tell()
print("當前位置:",position)


f.seek(4,0)  #從頭開始,偏移4個字節
position=f.tell()
print("當前位置:",position)

 
f.seek(4,1)  #從當前位置開始,偏移4個字節
position=f.tell()
print("當前位置:",position)

f.seek(-4,2)
position=f.tell()
print("當前位置:",position)


f.close()

運行結果:

總結:

seek中whence參數的值:

0:open函數以r,w,帶b的二進制模式,就是以任何模式打開文件,都能正常運行

1和2:open函數只能以二進制模式打開文件,才能正常運行,否則就會報出上面的錯誤

 

Python的官方文檔的解釋:

鏈接地址:https://docs.python.org/3/tutorial/inputoutput.html?highlight=seek

>In text files (those opened without a b in the mode string), only seeks relative to the beginning of the file are allowed (the exception being seeking to the very file end with seek(0, 2)) and the only valid offset values are those returned from the f.tell(), or zero. Any other offset value produces undefined behaviour.

翻譯:

    在文本文件中(那些在模式字符串中沒有b打開的文件),只允許相對於文件的開頭進行查找(例外情況是使用seek(0,2)查找文件的結尾),並且唯一有效的偏移值是從f.tell()或零返回的偏移值。任何其他偏移值都會產生未定義的行為。

 

 

 

StringIO和BytesIO

   StringIO和BytesIO是在內存中操作str 和bytes(注意:加了s)的方法,使得和讀寫文件具有一致的接口。

 

寫入時,可以用write 寫入,而查看寫入的值:使用 getvalue( )函數

讀取時,查看值,使用 readline( )、read( )函數

 

 StringIO

 只能在內存中操作字符串str

1 from io import StringIO 2 
3 f=StringIO() 4 f.write('Hello') 5 f.write(' , ') 6 f.write('World') 7 print(f.getvalue())

 

 

要讀取StringIO,可以用一個str初始化StringIO,然后,像讀文件一樣讀取:

1 # read from StringIO:
2 from io import StringIO 3 f = StringIO('水面細風生,\n菱歌慢慢聲。\n客亭臨小市,\n燈火夜妝明。') 4 while True: 5     s=f.readline() 6     if s=="": 7         break
8     print('start to print:') 9     print(s.strip())

運行結果: 可以看出是一行一行打印出來的

 

BytesIO

 BytesIO在內存中讀寫bytes

 注意:1、utf-8編碼,使用的是encode參數,中間是英文句號點(.)

            2、寫入的不是str,而是經過UTF-8編碼的bytes。

1 from io import BytesIO 2 
3 f=BytesIO() 4 f.write('中文'.encode('utf-8')) 5 print(f.getvalue())

 

和StringIO類似,可以用一個bytes初始化BytesIO,然后,像讀文件一樣讀取:

1 from io import BytesIO 2 f=BytesIO(b'\xe4\xb8\xad\xe6\x96\x87') 3 print(f.read())

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM