對于大文本文件的讀寫有哪些高效的方法?
結論:大文本文件,遠超內存,需要格外注意。
前言生產服務器中經常產生很多文件,有些積年累月單個文件,體積越來越大。
本文使用多種編程語言,實現大文件的讀取。
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效率高呢,因為其與普通函數的有區別:
function每次都是從第一行開始運行,而generator從上一次yield開始的地方運行。
function調用一次返回一個(一組)值,而generator可以多次返回。
function可以被無數次重復調用,而一個generator實例在yield最后一個值或者return之后就不能繼續調用了。
下面是使用帶有yield關鍵字的讀取大文件方法。
其實python還有更優雅便捷的寫法,就是for循環讀取。像下面這樣:
或者不用重新定義函數,直接用for循環遍歷。
底層使用的原理是一樣的。
Golang的方式Golang的想法也一樣,它絕不把整個文件寫入內存。bufio庫就是讀寫文件的利器。
不多解釋,直接上示例。
Golang為了統一場景,無論普遍的和特殊的,都考慮在內了。這就是bufio的厲害之處。
對于讀寫,數據被存儲直到達到特定大小,通過這種方式觸發的寫操作更少。同時還減少了 sycall(系統調用)的數量,卻可以使用更高效的方式使用底層硬件。
Linux下的工具如果你在Linux處理該文本,那這完全不是問題。自帶的很多工具,能夠幫你處理這個簡單的問題。比如 head,讀取某文件的前多少行;tail,讀取某文件的最后多少行。
如果只是要讀文件,那么 less 是你最佳的選擇。
文檔內說的很清楚,less并不直接讀入整個文檔,因此處理起來,比vi/vim編輯器速度快的多。
打開之后,就可以上下翻頁閱讀了。
結語本文通過多種編程語言,實現了大文件的讀取。
掌握核心的方法后,使用哪種語言實現,都可以。
【本文由 @程序員小助手 發布,持續分享編程與程序員成長相關的內容,歡迎關注】