我正在编写一个导入脚本,处理的文件可能有数十万行(日志文件)。使用下面这种非常简单的方法需要足够的时间和内存,以至于我感觉它随时都可能让我的MBP崩溃,所以我终止了这个进程。
#...
File.open(file, 'r') do |f|
f.each_line do |line|
# do stuff here to line
end
end
这个文件特别有 642,868 行:
$ wc -l nginx.log /code/src/myimport
642868 ../nginx.log
有人知道一种更高效(内存/CPU)的方法来处理此文件中的每一行吗?
更新
上面的 f.each_line
中的代码只是针对每一行匹配一个正则表达式。如果匹配失败,则将该行添加到 @skipped
数组中。如果匹配成功,则将匹配结果格式化为哈希表(由匹配的“字段”作为键),并将其附加到 @results
数组中。
# regex built in `def initialize` (not on each line iteration)
@regex = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) - (.{0})- \[([^\]]+?)\] "(GET|POST|PUT|DELETE) ([^\s]+?) (HTTP\/1\.1)" (\d+) (\d+) "-" "(.*)"/
#... loop lines
match = line.match(@regex)
if match.nil?
@skipped << line
else
@results << convert_to_hash(match)
end
我完全可以接受这是一个低效的过程。我可以让convert_to_hash
内部的代码使用预先计算好的lambda表达式,而不是每次都重新计算。我猜我只是认为问题在于行迭代本身,而不是每行代码。
each_line
这种方式。你可以按块读取文件,这样更快,然后使用String#lines
来获取单独的行,并重新连接任何部分加载的跨越块边界的行。这样做需要拆分行并重新连接断开的行,但总体来说效果相当。 - the Tin Man