如何在Ruby中从下到上读取文件?
我一直在研究Rails应用程序的日志查看器,并发现我需要从底部到顶部读取大约200行日志文件,而不是默认的顶部到底部。如何在Ruby中从下到上读取文件?
日志文件可能会变得很大,所以我已经尝试并排除了IO.readlines(“log_file.log”)[ - 200 ..- 1]方法。
是否有任何其他方法可以在不需要插件或gem的情况下向后读取文件?
唯一正确的方法来做到这一点,也适用于巨大的文件是从结尾读取n字节,直到你有你想要的行数。这实际上是Unix tail
的工作原理。
一个例子实施IO#tail(n)
,返回最后n
线作为Array
:
class IO
TAIL_BUF_LENGTH = 1 << 16
def tail(n)
return [] if n < 1
seek -TAIL_BUF_LENGTH, SEEK_END
buf = ""
while buf.count("\n") <= n
buf = read(TAIL_BUF_LENGTH) + buf
seek 2 * -TAIL_BUF_LENGTH, SEEK_CUR
end
buf.split("\n")[-n..-1]
end
end
的实施是一个有点天真,但快速的基准测试显示有什么可笑的差异这种简单的实现已经可以使(
user system total real
f.readlines[-200..-1] 7.150000 1.150000 8.300000 ( 8.297671)
f.tail(200) 0.000000 0.000000 0.000000 ( 0.000367)
基准代码:
用)与yes > yes.txt
生成一个〜25MB文件测试
require "benchmark"
FILE = "yes.txt"
Benchmark.bmbm do |b|
b.report "f.readlines[-200..-1]" do
File.open(FILE) do |f|
f.readlines[-200..-1]
end
end
b.report "f.tail(200)" do
File.open(FILE) do |f|
f.tail(200)
end
end
end
当然,other implementations已经存在。我没有尝试过,所以我不能告诉你哪个是最好的。
我想你的意思'TAIL_BUF_LENGTH = 2 ** 16'或'1 2010-06-11 18:06:09
工程太棒了!与readlines相比,基准差异是疯狂的。 是否可以在结果数组中输出每一行的相应行号? 谢谢! – ericalli 2010-06-11 18:41:44
@ two2twelve:不,它不是。整个练习的*全部目的*是从“从下到上”阅读文件。 (你的话,不是我的。)你怎么知道你是哪一行(从文件的顶部*开始计算的),如果你从*底部开始*?还是你的意思是从底部向上计数?在这种情况下,很容易:缓冲区中位于索引“i”的行是从底部开始的第n行。 – 2010-06-12 01:38:13
因为我太新来评论molf真棒的答案,我必须将其作为单独的答案发布。 我需要此功能来读取日志文件,而日志的最后部分包含字符串,我需要知道它已完成,我可以开始解析它。
因此处理小型文件对我来说至关重要(我可能会在日志很小的时候执行ping操作)。 所以我增强MOLF代码:
class IO
def tail(n)
return [] if n < 1
if File.size(self) < (1 << 16)
tail_buf_length = File.size(self)
return self.readlines.reverse[0..n-1]
else
tail_buf_length = 1 << 16
end
self.seek(-tail_buf_length,IO::SEEK_END)
out = ""
count = 0
while count <= n
buf = self.read(tail_buf_length)
count += buf.count("\n")
out += buf
# 2 * since the pointer is a the end , of the previous iteration
self.seek(2 * -tail_buf_length,IO::SEEK_CUR)
end
return out.split("\n")[-n..-1]
end
end
重复的:?阅读在Ruby文件的最后n行(http://stackoverflow.com/questions/754494) – hippietrail 2012-11-05 17:51:56