02.28 對於大文本文件的讀寫有哪些高效的方法?

程序員小助手


結論:大文本文件,遠超內存,需要格外注意。


前言

生產服務器中經常產生很多文件,有些積年累月單個文件,體積越來越大。

本文使用多種編程語言,實現大文件的讀取。


PHP的處理方式

大文件如果直接open,就會被整個寫入內存,內存是吃不消的。比如4G的內存,10G的文件,這是處理不了的。也沒有哪個文本編輯器可以用。

常用的file_get_contents函數,是將文件內容讀入到字符串變量內。而字符串變量分配在內存,所以沒有任何辦法處理大文件。

一般的做法是使用 fgets 函數。該函數從文件內讀取一行。函數格式如下:

string fgets ( resource [, int ] )

其中參數 $handle 是文件指針,從 $handle 指向的文件中讀取一行並返回長度最多為 $length - 1 字節的字符串。

碰到換行符(包括在返回值中)、EOF 或者已經讀取了 $length - 1 字節後停止(看先碰到那一種情況)。如果沒有指定 $length,則默認為 1K,或者說 1024 字節。

實際用起來像下面這樣。

此函數效率極高,一次最多讀一行。而碰到整個文件都是一行的情況,則按照設定的 $length 按照長度依次讀取。


Python的方式

從上述PHP的處理方法可以看到,關鍵點是按行讀取,再按長度讀取。python的處理方法一樣,只是摻雜了一些語法糖。

python有一個“生成器”,解決大文件的讀取,就靠這個玩意兒。

在python的函數(function)定義中,只要出現了yield表達式(Yield expression),那麼事實上定義的是一個generator function, 調用這個generator function返回值是一個generator。

為什麼genetor效率高呢,因為其與普通函數的有區別:

  1. function每次都是從第一行開始運行,而generator從上一次yield開始的地方運行。

  2. function調用一次返回一個(一組)值,而generator可以多次返回。

  3. function可以被無數次重複調用,而一個generator實例在yield最後一個值或者return之後就不能繼續調用了。

下面是使用帶有yield關鍵字的讀取大文件方法。

其實python還有更優雅便捷的寫法,就是for循環讀取。像下面這樣:

或者不用重新定義函數,直接用for循環遍歷。

底層使用的原理是一樣的。


Golang的方式

Golang的想法也一樣,它絕不把整個文件寫入內存。bufio庫就是讀寫文件的利器。

不多解釋,直接上示例。

Golang為了統一場景,無論普遍的和特殊的,都考慮在內了。這就是bufio的厲害之處。

對於讀寫,數據被存儲直到達到特定大小,通過這種方式觸發的寫操作更少。同時還減少了 sycall(系統調用)的數量,卻可以使用更高效的方式使用底層硬件。


Linux下的工具

如果你在Linux處理該文本,那這完全不是問題。自帶的很多工具,能夠幫你處理這個簡單的問題。比如 head,讀取某文件的前多少行;tail,讀取某文件的最後多少行。

如果只是要讀文件,那麼 less 是你最佳的選擇。

文檔內說的很清楚,less並不直接讀入整個文檔,因此處理起來,比vi/vim編輯器速度快的多。

打開之後,就可以上下翻頁閱讀了。


結語

本文通過多種編程語言,實現了大文件的讀取。

掌握核心的方法後,使用哪種語言實現,都可以。


【本文由 @程序員小助手 發佈,持續分享編程與程序員成長相關的內容,歡迎關注】

程序員小助手


對於大文本文件(超過內存大小的文件),通常是無法直接加載至內存的。所以問題就不在用何種編程語言,而在於用什麼方法了。我通常用以下方法:

  1. 首先將大文件進行分割,並打上標記。

  2. 然後使用多線程對各個文件進行處理。

  3. 最後將處理的結果進行統計。

如果你用到JAVA的話,我覺得可以使用以下兩種方法進行讀操作:

  • 傳統的Block IO,這個可以直接讀取單個大文件,直接交由JAVA的BufferedReader去處理。(這種方式是線程阻塞的,請注意異常處理。)

  • NIO方式,這種也是我上面提到的方式,拆分文件,利用字節流數組,處理拆分後的文件,再進行彙總處理。(這種方式要注意拆分的文件不要過大。)


李老師tome


有與低級文件流接口的語言即可,典型如C/C++。很多高級語言都有,看你用哪種了。處理方式,都是分段讀寫,不二之選,不要一下子讀入內存。


TonyDeng


用RandomAccessfile的類方法進行讀取文件,會比一般的方法快很多,再用bufferwriter把得到的結果寫進txt中。


分享到:


相關文章: