在Ruby中搜索大文件的方法更简单?

时间:2013-05-06 13:44:33

标签: ruby

我正在编写一个简单的日志嗅探器,它将搜索日志以查找指示我支持的软件问题的特定错误。它允许用户指定日志的路径并指定他们想要搜索的天数。

如果用户关闭了日志滚动,则日志文件有时会变得非常大。目前我正在做以下事情(虽然尚未完成):

File.open(@log_file, "r") do |file_handle|
    file_handle.each do |line|
        if line.match(/\d+++-\d+-\d+/)
          etc...

line.match显然会查找我们在日志中使用的日期格式,其余逻辑将在下面。但是,有没有更好的方法来搜索没有.each_line的文件?如果没有,我对此完全没问题。我只是想确保我使用的是最好的资源。

由于

6 个答案:

答案 0 :(得分:5)

  • fgrep作为独立版或从system('fgrep ...')调用可能是更快的解决方案
  • file.readlines速度可能更好,但这是一个时空权衡
  • 看看this很少的研究 - 最后的方法看起来相当快。

答案 1 :(得分:2)

以下是一些编码提示......

而不是:

File.open(@log_file, "r") do |file_handle|
  file_handle.each do |line|

使用:

File.foreach(@log_file) do |line|
  next unless line[/\A\d+++-\d+-\d+/]

foreach简化了文件的打开和循环。

next unless...制作一个紧密循环,跳过不以目标字符串开头的每一行。在确定你是否有一条好线之前你做的越少,你的代码运行得越快。

在模式的开头使用锚点,例如\A为正则表达式引擎提供了关于在该行中查找位置的主要提示,并允许它在线路不匹配时快速拯救。此外,使用line[/\A\d+++-\d+-\d+/]更简洁一点。

答案 2 :(得分:1)

如果日志文件按日期排序,则可以避免通过二进制搜索搜索整个文件。在这种情况下你要:

  1. 打开文件,就像你正在做的那样
  2. 使用lineo=快进到文件的中间位置。
  3. 检查线条上的日期是高于还是低于您要查找的日期。
  4. 继续将文件分成两半,直到找到所需内容。
  5. 我认为你的文件需要非常大才能使上述内容有意义。

    修改

    以下是一些显示基本概念的代码。它找到一行包含搜索日期,而不是第一行。这可以通过更多二进制搜索或从最后一个中间点(不包含日期)进行线性搜索来解决。如果日期不在文件中,也没有终止条件。这些小小的补充,留给读者练习: - )

    require 'date'
    
    def bin_fsearch(search_date, file)
      f = File.open file
    
      search = {min: 0, max: f.size}
    
      while true
        # go to file midpoint
        f.seek (search[:max] + search[:min]) / 2
    
        # read in until EOL
        f.gets
    
        # record the actual mid-point we are using
        pos = f.pos
    
        # read in next line
        line = f.gets
    
        # get date from line
        line_date = Date.parse(line)
    
        if line_date < search_date
          search[:min] = f.pos
        elsif line_date > search_date
          search[:max] = pos
        else
          f.seek pos
          return
        end
      end
    end
    
    bin_fsearch(Date.new(2013, 5, 4), '/var/log/system.log')
    

答案 3 :(得分:0)

试试这个,它会一次搜索一次&amp;应该很快&amp;占用更少的内存。

File.open(file, 'r') do |f|
  f.each_line do |line|
    # do stuff here to line
  end
end

另一个更快的选择是将整个文件读入一个数组。它会很快,但需要很多记忆。

File.readlines.each do |line|
  #do stuff with each line
end

此外,如果您需要内存最少的最快方法,请尝试grep专门用于搜索大文件。所以应该快速和记忆反应

`grep -e regex bigfile`.split(/\n/).each do |line|
  # ... (called on each matching line) ...
end

答案 4 :(得分:0)

比逐行更快地读取行:

File.open('file.txt') do |f|
  buff = f.read(10240)
  # ...
end

但是你使用regexp来匹配日期,你可能会得到不完整的行。你必须在你的逻辑中处理它。

此外,如果性能非常重要,请考虑编写一个非常简单的C扩展。

答案 5 :(得分:0)

如果日志文件变得庞大,那么您可能会担心,那么您可以考虑将错误保存在数据库中。然后,您将获得更快的响应。