Ruby:开始在大文件中的任意点读取

我有一些我希望筛选的日志文件。 内容正是您在日志文件中所期望的:许多单行的逗号分隔文本。 这些文件大约每个4演出。 其中一个File.each_line或foreach大约需要20分钟。

由于简单的foreach似乎……简单(而且速度慢),我认为如果我只能告诉他们从哪里开始,两个单独的线程可能能够在同一个文件上工作。 但基于我(有限的)知识,我无法决定这是否可能。

有没有办法开始在任意行读取文件?

对于行,它可能有点困难,但您可以在文件中寻找某个字节。

IO#seek (链接)和IO#pos (链接)都允许您搜索文件中的给定字节。

为了看看整个文件一次又一次地诋毁整个文件有什么区别,我测试了一个大约99MB的文件,超过1,000,000行。

 greg-mbp-wireless:Desktop greg$ wc filelist.txt 1003002 1657573 99392863 filelist.txt 

我将以下循环放入ruby文件中,并使用time命令从命令行运行它:

 IO.read(ARGV.first).lines { |l| } greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt real 0m1.411s user 0m0.653s sys 0m0.169s 

然后我把它改为逐行读取并定时:

 IO.readlines(ARGV.first) { |l| } greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt real 0m1.053s user 0m0.741s sys 0m0.278s 

我不确定为什么,但逐行阅读更快。 这可能与内存分配有关,因为Ruby在第一个示例中尝试将整个文件加载到RAM中,或者可能是exception,因为我只为每个文件执行了一次测试。 使用带有显式文件大小的read可能会更快,因为Ruby会知道它需要提前分配多少。

这就是我需要测试的全部内容:

 fcontent = '' File.open(ARGV.first, 'r') do |fi| fsize = fi.size fcontent = fi.read(fsize) end puts fcontent.size greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt 99392863 real 0m0.168s user 0m0.010s sys 0m0.156s 

看起来知道需要阅读多少会产生很大的不同。

在字符串缓冲区中添加回循环会导致:

 File.open(ARGV.first, 'r') do |fi| fsize = fi.size fi.read(fsize).lines { |l| } end greg-mbp-wireless:Desktop greg$ time ruby test.rb filelist.txt real 0m0.732s user 0m0.572s sys 0m0.158s 

这仍然是一个进步。

如果您使用Queue并从负责读取文件的线程中提供它,那么从传入文本的任何进程中消耗队列,那么您可能会看到更高的总吞吐量。

如果你想从文件中的特定行开始,我建议你只需要尾随。

 excerpt = `tail -m +5000 filename.log` 

这将为您提供从第5000行到文件末尾的filename.log的内容。

尝试使用faster_csv,如果你还没有,如果那仍然太慢,那么在c中使用具有原生扩展名的东西 – http://github.com/wwood/excelsior