Ruby 如何从文件中获取特定行

Ruby 如何从文件中获取特定行,ruby,file-io,Ruby,File Io,是否可以在知道行号的情况下从文件中提取特定行?例如,只需从文件“text.txt”中以字符串形式获取第N行的内容即可尝试以下两种解决方案之一: file = File.open "file.txt" #1 solution would eat a lot of RAM p [*file][n-1] #2 solution would not n.times{ file.gets } p $_ file.close 您可以通过索引从readlines获取它 line = IO.readli

是否可以在知道行号的情况下从文件中提取特定行?例如,只需从文件“text.txt”中以字符串形式获取第
N
行的内容即可

尝试以下两种解决方案之一:

file = File.open "file.txt"

#1 solution would eat a lot of RAM
p [*file][n-1]

#2 solution would not
n.times{ file.gets }
p $_

file.close

您可以通过索引从
readlines
获取它

line = IO.readlines("file.txt")[42]
只有当它是一个小文件时才使用它

def get_line_from_file(path, line)
  result = nil

  File.open(path, "r") do |f|
    while line > 0
      line -= 1
      result = f.gets
    end
  end

  return result
end

get_line_from_file("/tmp/foo.txt", 20)
这是一个很好的解决方案,因为:

  • 您不使用
    File.read
    ,因此不会将整个文件读入内存。如果文件大小为20MB,并且您读取的次数足够多,因此GC无法跟上,那么这样做可能会成为一个问题
  • 您只能从文件中读取到所需的行。如果您的文件有1000行,那么获取第20行只会将前20行读入Ruby
如果希望引发错误(
eoferor
),而不是在传递越界行时返回nil,则可以将
gets
替换为
readline

linenumber=5
open("file").each_with_index{|line,ind|
  if  ind+1==linenumber
    save=line
    # break or exit if needed.
  end
}

如果你只想得到这条线而不想做其他事情,你可以使用这条线

ruby -ne '(print $_ and exit) if $.==5' file

文件有一个很好的
lineno
方法

def get_line(filename, lineno)
  File.open(filename,'r') do |f|
     f.gets until f.lineno == lineno - 1
     f.gets
  end
end

如果只需要一行,而不关心内存使用情况,请使用(假设行从1开始编号)

如果您已经打开了文件

否则,最好这样做:

lineN = File.open('text.txt') do |f|
          (n-1).times { f.gets } # skip lines preceeding line N
          f.gets                 # read line N contents
        end

如果您只想从一个文件中读取一行,或者希望从一个小到可以重复读取的文件中读取多行,则这些解决方案可以工作。大型文件(例如,1000万行)搜索特定行需要更长的时间,因此最好在一次读取中按顺序获取必要的行,这样大型文件就不会被多次读取

创建一个大文件:

File.open('foo', 'a') { |f| f.write((0..10_000_000).to_a.join("\n")) }
选择要从中读取的行,并确保它们已排序:

lines = [9_999_999, 3_333_333, 6_666_666].sort
打印这些行:

File.open('foo') do |f|
  lines.each_with_index do |line, index|
    (line - (index.zero? ? 0 : lines[index - 1]) - 1).times { f.gets }
    puts f.gets
  end
end

此解决方案适用于任意数量的行,不将整个文件加载到内存中,读取的行数尽可能少,并且只读取文件一次。

Hehe,这是您在尝试编写优化代码时从Ruby同事那里得到的信息(嗯?:)我认为,在任何语言中,将整个20MB文件读入内存以从中获得一行都是一种糟糕的做法。@August Lilleaas,我认为,在这里,你不需要
result=nil
return
和编写C风格的循环,只要你有
。谢天谢地,我可以按照我想要的方式编写任意多的ruby,我不必听货运爱好者们要我做什么:)@Augustilleas然而,当你使用一种语言时,你应该用惯用的方式来写,即使只是因为语言往往比其他语言更优化惯用的结构。此外,如果有人打算维护您的代码,那么不学习正确的方法是一种伤害。解决方案2是否得到了n+1行?@Mark Thomas,in#1。我假设索引是从0开始的。感谢
[*File.open(“…”)]
,我不知道
到_a
的文件实例可以给我它的lines@Nakilon:关于#2,文件行从1(所有编辑器,甚至
cat-n
都这样做)索引:我认为unplat是一项代价高昂的操作。另外,语法也很混乱。这就是全局$-变量变得有用的时候了。这会在已经找到行时继续读取文件。没关系。例如,如果行号是最后的第二行,那么它也必须一直读到那一行……实际上不需要lineno()。您可以将'until'行替换为
(lineno-1).times{f.gets}
。只有当文件很小,小于几MB时,这才是正确的答案。否则,它将强制Ruby一次加载整个文件,对于大文件,这比使用基于
foreach
gets
的解决方案要慢。看看哪一个包含基准测试。我惊讶地发现它需要几MB的内存!我真的被下面的很多答案误导了。嗯,最好的评论如下:没有内存问题,直截了当。
File.open('foo', 'a') { |f| f.write((0..10_000_000).to_a.join("\n")) }
lines = [9_999_999, 3_333_333, 6_666_666].sort
File.open('foo') do |f|
  lines.each_with_index do |line, index|
    (line - (index.zero? ? 0 : lines[index - 1]) - 1).times { f.gets }
    puts f.gets
  end
end