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中。


分享到:


相關文章: