【问题标题】:How to get a particular line from a file如何从文件中获取特定行
【发布时间】:2011-04-30 04:26:43
【问题描述】:

是否可以从知道其行号的文件中提取特定行?例如,只需从文件“text.txt”中获取N 行的内容作为字符串?

【问题讨论】:

  • 我真的被下面的很多答案误导了。恕我直言,最好的是this answer下面的评论:没有内存问题,直截了当。

标签: ruby file-io


【解决方案1】:

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

line = IO.readlines("file.txt")[42]

只有在文件很小的情况下才使用它。

【讨论】:

  • 只有在文件很小(小于几 MB)的情况下,这才是正确的答案。否则它将强制 Ruby 一次加载整个文件,在大文件的情况下,这比使用基于 foreachgets 的解决方案要慢。请参阅包含基准的stackoverflow.com/questions/25189262/why-is-slurping-a-file-bad
  • 我惊讶地发现它需要几个 MB!
【解决方案2】:

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

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

【讨论】:

  • 解决方案 #2 得到第 n+1 行了吗?
  • @Mark Thomas,也在#1。我应该从 0 开始索引。
  • 感谢[*File.open('…')],不知道文件实例的to_a 可以给我它的台词
  • @Nakilon:文件行从 1 开始索引(所有编辑器甚至 cat -n 都这样做)
  • 关于#2:我认为 unsplat 的操作成本很高。此外,语法令人困惑。
【解决方案3】:
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。

如果您想引发错误 (EOFError) 而不是在传递越界线时返回 nil,则可以将 gets 替换为 readline

【讨论】:

  • 呵呵,当您尝试编写优化代码时,这就是您从其他 Ruby 爱好者那里得到的结果吧? :)
  • 我认为将整个 20MB 文件读入内存以从中获取一行在任何语言中都是不好的做法。
  • @August Lilleaas,我认为,当你有 times 时,你不需要 result = nilreturn 并编写 C 风格的循环。
  • 谢天谢地,我可以随心所欲地写出我想要的红宝石,而且我不必听货物狂热者要我做什么 :)
  • @AugustLilleaas 但是,当您使用一种语言时,您应该使用惯用语来编写它,这仅仅是因为语言通常比其他语言更能优化惯用语结构。此外,如果有人要维护您的代码,那么不学习正确的方法是一种伤害。
【解决方案4】:

文件有一个很好的lineno 方法。

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

【讨论】:

  • 你并不需要 lineno()。你可以用(lineno-1).times {f.gets}替换'直到'行。
【解决方案5】:
linenumber=5
open("file").each_with_index{|line,ind|
  if  ind+1==linenumber
    save=line
    # break or exit if needed.
  end
}

linenumber=5
f=open("file")
while line=f.gets
  if $. == linenumber # $. is line number
    print "#{f.lineno} #{line}" # another way
    # break  # break or exit if needed
  end
end
f.close

如果你只是想拿线什么都不做,你可以使用这一个班轮

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

【讨论】:

  • 不错。这就是全局 $-variables 变得有用的时候。
  • 当已经找到该行时,它会继续读取文件。
  • 没关系。例如,如果行号是最后第二行,它也必须读取到该行...
【解决方案6】:

如果你想要一个班轮并且不关心内存使用情况,请使用(假设行从 1 开始编号)

lineN = IO.readlines('text.txt')[n-1]

lineN = f.readlines[n-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

【讨论】:

    【解决方案7】:

    如果您只需要文件中的一行,或者如果您希望文件中的多行小到可以重复读取,这些解决方案就可以使用。大文件(例如 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
    

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

    【讨论】:

      猜你喜欢
      • 2020-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-05
      • 1970-01-01
      • 1970-01-01
      • 2022-12-05
      相关资源
      最近更新 更多