有没有办法在不将整个内容加载到数组中的情况下搜索文件?

时间:2013-05-24 07:19:34

标签: ruby

这有效:

f = File.new("myfile").readlines
f[0] #=> "line 1"
f[21] #=> "line 22"

但是,如果我有一个非常大的文件,只需要阅读几行。是否有可能寻找特定的行并在Ruby中读取它们,而无需将文件加载到数组中?

我知道IO流,其中(如stdin的情况)你不能随意搜索流。当然必须有一种方法可以在不加载整个文件的情况下执行此操作。

3 个答案:

答案 0 :(得分:7)

不要忽略IO类。 IO::foreach是返回枚举器的方法之一,可以进行延迟评估。

IO#each_line也是另一个将返回枚举器的人。

在Ruby 2.0中,我们可以调用.lazy并使用那些方法(zip和cycle除外),这些方法允许我们遍历枚举而不将整个文件放入内存。

答案 1 :(得分:5)

出于此目的,您可以使用each_line迭代器与with_index结合使用当前行的行号(从0开始计算):

File.open('myfile') do |file|

  file.each_line.with_index do |line, lineno|
    case lineno
    when 0
      # line 1
    when 21
      # line 22
    end   
  end

end

通过使用open,将块传递给它而不是new,可以保证在块执行结束时正确关闭文件。


更新 with_index方法接受一个可选参数来指定要使用的起始索引,因此上面的代码可以更好地编写如下:

file.each_line.with_index(1) do |line, lineno|
  case lineno
  when 1
    # line 1
  end
end

答案 2 :(得分:2)

我使用过Jack和toro2k的答案(大致相同的答案),但是根据我自己的用例进行了修改。我可能想要的地方:打开一个文件,并寻找多个随机行,其中顺序可能并不总是顺序的。这就是我提出的(抽象的):

class LazyFile
    def initialize(file)
        @content = File.new(file)
    end

    def [](lineno)
        @content.rewind if @content.lineno > lineno
        skip = lineno - @content.lineno
        skip.times { @content.readline }
        @content.readline
    end
end

file = LazyFile("myfile")
file[1001]