rfu*_*duk 36
如果在*nix系统上tail
,你可以这样作弊:
last_25_lines = `tail -n 25 whatever.txt`
Run Code Online (Sandbox Code Playgroud)
Bri*_*ell 25
文件是否足够大,您需要避免阅读整个文件?如果没有,你可以这样做
IO.readlines("file.log")[-25..-1]
Run Code Online (Sandbox Code Playgroud)
如果它很大,你可能需要使用IO#seek
从文件末尾读取,并继续寻找开头,直到你看到25行.
Joh*_*n T 14
Ruby有一个名为File :: Tail的库.这可以像UNIX尾部实用程序一样获取文件的最后N行.
我假设在UNIX版本的tail中有一些搜索优化,有这样的基准测试(在超过11M的文本文件上测试):
[john@awesome]$du -sh 11M.txt
11M 11M.txt
[john@awesome]$time tail -n 25 11M.txt
/sbin/ypbind
/sbin/arptables
/sbin/arptables-save
/sbin/change_console
/sbin/mount.vmhgfs
/misc
/csait
/csait/course
/.autofsck
/~
/usb
/cdrom
/homebk
/staff
/staff/faculty
/staff/faculty/darlinr
/staff/csadm
/staff/csadm/service_monitor.sh
/staff/csadm/.bash_history
/staff/csadm/mysql5
/staff/csadm/mysql5/MySQL-server-community-5.0.45-0.rhel5.i386.rpm
/staff/csadm/glibc-common-2.3.4-2.39.i386.rpm
/staff/csadm/glibc-2.3.4-2.39.i386.rpm
/staff/csadm/csunixdb.tgz
/staff/csadm/glibc-headers-2.3.4-2.39.i386.rpm
real 0m0.012s
user 0m0.000s
sys 0m0.010s
Run Code Online (Sandbox Code Playgroud)
我只能想象Ruby库使用类似的方法.
编辑:
对于Pax的好奇心:
[john@awesome]$time cat 11M.txt | tail -n 25
/sbin/ypbind
/sbin/arptables
/sbin/arptables-save
/sbin/change_console
/sbin/mount.vmhgfs
/misc
/csait
/csait/course
/.autofsck
/~
/usb
/cdrom
/homebk
/staff
/staff/faculty
/staff/faculty/darlinr
/staff/csadm
/staff/csadm/service_monitor.sh
/staff/csadm/.bash_history
/staff/csadm/mysql5
/staff/csadm/mysql5/MySQL-server-community-5.0.45-0.rhel5.i386.rpm
/staff/csadm/glibc-common-2.3.4-2.39.i386.rpm
/staff/csadm/glibc-2.3.4-2.39.i386.rpm
/staff/csadm/csunixdb.tgz
/staff/csadm/glibc-headers-2.3.4-2.39.i386.rpm
real 0m0.350s
user 0m0.000s
sys 0m0.130s
Run Code Online (Sandbox Code Playgroud)
仍然不到一秒,但如果有很多文件操作,这会产生很大的不同.
小智 8
改进版manveru卓越的基于搜索的解决方案.这个正好返回n行.
class File
def tail(n)
buffer = 1024
idx = [size - buffer, 0].min
chunks = []
lines = 0
begin
seek(idx)
chunk = read(buffer)
lines += chunk.count("\n")
chunks.unshift chunk
idx -= buffer
end while lines < ( n + 1 ) && pos != 0
tail_of_file = chunks.join('')
ary = tail_of_file.split(/\n/)
lines_to_return = ary[ ary.size - n, ary.size - 1 ]
end
end
Run Code Online (Sandbox Code Playgroud)
我刚写了一个快速实现#seek
:
class File
def tail(n)
buffer = 1024
idx = (size - buffer).abs
chunks = []
lines = 0
begin
seek(idx)
chunk = read(buffer)
lines += chunk.count("\n")
chunks.unshift chunk
idx -= buffer
end while lines < n && pos != 0
chunks.join.lines.reverse_each.take(n).reverse.join
end
end
File.open('rpn-calculator.rb') do |f|
p f.tail(10)
end
Run Code Online (Sandbox Code Playgroud)
这是一个尾部版本,在你去的时候不会在内存中存储任何缓冲区,而是使用"指针".也可以进行边界检查,这样你就不会最终寻找负偏移量(例如,如果你有更多的东西要阅读但是还要小于你的块大小).
def tail(path, n)
file = File.open(path, "r")
buffer_s = 512
line_count = 0
file.seek(0, IO::SEEK_END)
offset = file.pos # we start at the end
while line_count <= n && offset > 0
to_read = if (offset - buffer_s) < 0
offset
else
buffer_s
end
file.seek(offset-to_read)
data = file.read(to_read)
data.reverse.each_char do |c|
if line_count > n
offset += 1
break
end
offset -= 1
if c == "\n"
line_count += 1
end
end
end
file.seek(offset)
data = file.read
end
Run Code Online (Sandbox Code Playgroud)
测试用例:https://gist.github.com/shaiguitar/6d926587e98fc8a5e301
我不能保证 Ruby,但大多数这些语言都遵循文件 I/O 的 C 习惯用法。这意味着除了搜索之外没有其他办法可以完成您所要求的操作。这通常采用两种方法之一。
第二种方法是我更喜欢的方法,因为如果你明智地选择你的第一个偏移,你几乎肯定只需要一次尝试。日志文件仍然倾向于具有固定的最大行长度(我认为,在实用性下降很长时间之后,程序员仍然倾向于使用 80 列的文件)。我倾向于选择所需行数乘以 132 作为偏移量。
从 Ruby 在线文档的粗略浏览来看,它看起来确实遵循C 习惯用法。"ios.seek(25*-132,IO::SEEK_END)"
如果您遵循我的建议,您就会使用,然后从那里继续阅读。