[技巧篇] 如何在不會的情況下解釋 Python 設置文件的緩衝的問題

  • 2019 年 10 月 4 日
  • 筆記

美好的中秋,朋友圈在享受悠閑周末的時候,剛入門 Python 的程式設計師小R在公司埋頭加班,不知時間幾何,一不小心把下周的工作捯飭完了,心情愉悅,不能自拔。

這不,周一的早上,小R吹著口哨,泡著枸杞,幻想著划水到飛起,嘴角抑制不住的咧到了耳朵根兒,翹起了二郎腿。

正抖腿抖到舒坦的時候,微信收到了一條消息,點開一看,消息來自一個熟悉又陌生的名字,在記憶的排水溝里使勁掏了掏,好不容易對號入座。一個許久未曾聯繫的同學小K,消息只有兩個字:

「在嗎?」

看著這兩個字, 小R眉頭一皺,發現事情

怎麼回事?他這是要幹嘛?為什麼要問在嗎?

是要借錢嘛?是要結婚嘛?是要...

掙扎良久,小R放下了二郎腿,按下了鍵盤:

「在」

此時心裡已經在自導自演各種應對場景:

「要是借錢,我就...」

「要是結婚,我就...」

「....」

有一條消息過來,在它閃動的第24 次小R打開了聊天窗口:「聽說你是 Python 大佬,我有一個問題想問你。」

嘿,嚇死我了,你早說啊,此時小R內心一陣竊喜:「哎呀,我這麼低調,竟然被人發現大佬的身份啦,看來我還要再低調一點,哈哈哈哈哈哈...」

低調的小R低調的在窗口敲下了如下內容:

「哎呀,沒有啦,你有什麼問題,隨便問,沒有我不會的。」

enter 一鍵發送。

小K:「太好了,Python 裡面怎麼設置文件的緩衝啊?」

小R:「....」

文件我懂,緩衝我懂,怎麼設置,我哪知道怎麼設置...

小K:「還在嗎?」

好想說不在,當然認真負責的小R當然不會這樣,有句偉大的「名言」怎麼說的來著:自己的低調,跪著也要低調完...

於是,小R默默的打開了 Google...


00.文件的緩衝

如何設置文件的緩衝,先要知道什麼是文件的緩衝:

當我們將文件內容寫入到硬體設備的時候,我們需要系統調用(系統調用也就是向作業系統申請一個服務,作業系統響應以後,幫助我們調用硬體的驅動程式)來完成寫操作,這類操作我們也就是 I/O 操作。I/O 操作很耗時,為了提高效率,我們就要減少 I/O 操作的次數,我們使用的手段就是為文件設置一個緩衝區。

對於磁碟這種塊設備,它的讀寫不是一個一個的位元組,而是按「塊」。假設一個「塊」的大小是 4096 個位元組,你寫入一個位元組,或者寫入 4096 個位元組,都需要一次 I/O 操作,設置緩衝區,當寫入的數據不足一個「塊」大小時,都放入到緩衝區當中,等湊夠了一個「塊」的數據量,再調用一次 I/O 操作,這樣達到減少 I/O 操作的目的。

01.Python中文件對象的緩衝行為

文件的緩衝一般分為「全緩衝」、「行緩衝」、「無緩衝」。

「全緩衝」就是我在上面說的,緩衝區有一定大小,數據湊齊了這個大小就進行一次系統調用;「行緩衝」是在某些終端設備中上使用,碰到換行符進行一次系統調用;「無緩衝」是在一些不希望進行緩衝的設備上,比如串口設備,我們就需要及時把數據發送到串口上去。

下面我們就來看一下,在 Python 中默認的文件對象緩衝行為是怎樣的。

首先我們創建一個 test.txt 的文件,以「只寫」的形式打開:

f = open('test.txt','w')

然後我們來觀察一下文件中的內容(此時應該為空):

接下來我們向 test.txt 中寫入一些內容:

f = open('test.txt','w')  f.write('abc')

我們再來看一下文件中的內容:

仍然沒有輸出,這就意味著 「abc」 並沒有真實的寫入到磁碟中,而是進入到了緩衝區,其實到這你可以探測一下緩衝區的大小,通常一個「塊」的大小為 4096 個位元組,你可以嘗試寫入來試驗,直到 tail -f test.txt 有內容顯示為止。

這就是普通文件默認的緩衝行為,緩衝區的大小是根據平台和自身的屬性相關的。在某些時候,我們需要改變緩衝區的大小,該怎麼做呢?

其實很好解決:在 open 函數中,有個 buffering 參數,可以讓這個問題迎刃而解。

我們將 buffering 設置為大於 1 的整數 n(n 為緩衝區的大小),這就是「全緩衝」;將 buffering 設置為 1,這就是「行緩衝」;將 buffering 設置為 0,這就是「無緩衝」。

接下來我們就嘗試一下:

f = open('test1.txt','w',buffering=1024)  f.write('*'*512)

我們用同樣的「只讀」方式創建 test1.txt,設定緩衝區大小為 1024,我們先寫入 512 個 「*」,接下來看一下文件的內容:

接下來我們就湊齊 1024 個位元組:

f = open('test1.txt','w',buffering=1024)  f.write('*'*512)  f.write('*'*511)  f.write('/')

此時文件的內容為:

這就全部輸出出來了。

同理,對於「行緩衝」和「無緩衝」也是類似的操作,改變相應 buffering 的值即可,只是對於「行緩衝」,記得要寫入換行符的時候才會在文件中顯示出內容,感興趣的可以嘗試一下。

Tags: