【问题标题】:What are all the common ways to read a file in Ruby?在 Ruby 中读取文件的常用方法有哪些?
【发布时间】:2023-04-07 18:30:01
【问题描述】:

在 Ruby 中读取文件的常用方法有哪些?

例如,这是一种方法:

fileObj = File.new($fileName, "r")
while (line = fileObj.gets)
  puts(line)
end
fileObj.close

我知道 Ruby 非常灵活。每种方法的优缺点是什么?

【问题讨论】:

标签: ruby file-io


【解决方案1】:

如果文件不太长,最简单的方法是:

puts File.read(file_name)

确实,IO.readFile.read 会自动关闭文件,因此无需将File.open 与块一起使用。

【讨论】:

  • IO.readFile.read 也会自动关闭文件,尽管您的措辞听起来好像没有。
  • 他已经说过“如果文件不太长”。非常适合我的情况。
【解决方案2】:
File.open("my/file/path", "r") do |f|
  f.each_line do |line|
    puts line
  end
end
# File is closed automatically at end of block

也可以在如上之后显式关闭文件(将块传递给open 为您关闭它):

f = File.open("my/file/path", "r")
f.each_line do |line|
  puts line
end
f.close

【讨论】:

  • 这几乎不是惯用的 Ruby。使用foreach 而不是open 并省去each_line 块。
  • f.each { |line| ... }f.each_line { |line| ... } 似乎具有相同的行为(至少在 Ruby 2.0.0 中)。
【解决方案3】:

提防“slurping”文件。那是您一次将整个文件读入内存的时候。

问题在于它不能很好地扩展。您可能正在使用大小合理的文件开发代码,然后将其投入生产,突然发现您正在尝试读取以千兆字节为单位的文件,并且您的主机在尝试读取和分配内存时冻结。

逐行 I/O 速度非常快,而且几乎总是和 slurping 一样有效。实际上速度快得惊人。

我喜欢用:

IO.foreach("testfile") {|x| print "GOT ", x }

File.foreach('testfile') {|x| print "GOT", x }

文件继承自 IO,foreach 在 IO 中,可以任意使用。

我有一些基准测试显示尝试通过read 与“Why is "slurping" a file not a good practice?”处的逐行 I/O 读取大文件的影响。

【讨论】:

【解决方案4】:

您可以一次读取所有文件:

content = File.readlines 'file.txt'
content.each_with_index{|line, i| puts "#{i+1}: #{line}"}

当文件很大或可能很大时,通常最好逐行处理:

File.foreach( 'file.txt' ) do |line|
  puts line
end

有时您希望访问文件句柄或自己控制读取:

File.open( 'file.txt' ) do |f|
  loop do
    break if not line = f.gets
    puts "#{f.lineno}: #{line}"
  end
end

如果是二进制文件,您可以指定零分隔符和块大小,如下所示:

File.open('file.bin', 'rb') do |f|
  loop do
    break if not buf = f.gets(nil, 80)
    puts buf.unpack('H*')
  end
end

最后,您可以在没有块的情况下执行此操作,例如同时处理多个文件时。在这种情况下,必须明确关闭文件(根据@antinome 的评论进行改进):

begin
  f = File.open 'file.txt'
  while line = f.gets
    puts line
  end
ensure
  f.close
end

参考:File APIIO API

【讨论】:

  • 文件或 IO 中没有 for_each。请改用foreach
  • 我通常使用带有 RubyMarkers 插件的 Sublime Text 编辑器,在此处记录要用于答案的代码时。它使显示中间结果变得非常容易,类似于使用 IRB。 Sublime Text 2 的 Seeing Is Believing 插件也非常强大。
  • 很好的答案。对于最后一个示例,我可能建议使用while 而不是loop 并使用ensure 来确保即使引发异常也能关闭文件。像这样(用换行符替换分号):begin; f = File.open('testfile'); while line = f.gets; puts line; end; ensure; f.close; end.
  • 是的,@antinome 好多了,改进了答案。谢谢!
【解决方案5】:

一个简单的方法是使用readlines:

my_array = IO.readlines('filename.txt')

输入文件中的每一行都是数组中的一个条目。该方法为您处理打开和关闭文件。

【讨论】:

  • read 或任何变体一样,这会将整个文件拉入内存,如果文件大于可用内存,则可能会导致严重问题。另外,由于它是一个数组,Ruby 必须创建数组,这也进一步减慢了进程。
【解决方案6】:
file_content = File.read('filename with extension');
puts file_content;

http://www.ruby-doc.org/core-1.9.3/IO.html#method-c-read

【讨论】:

    【解决方案7】:

    我通常这样做:

    open(path_in_string, &:read)
    

    这会将整个文本作为字符串对象提供给您。它仅适用于 Ruby 1.9。

    【讨论】:

    • 这很好很简短!它是否也关闭了文件?
    • 它会关闭它,但它不可扩展,所以要小心。
    【解决方案8】:

    从 your_file.log 或 .txt 返回最后 n

    path = File.join(Rails.root, 'your_folder','your_file.log')
    
    last_100_lines = `tail -n 100 #{path}`
    

    【讨论】:

      【解决方案9】:

      一种更有效的方式是流式传输,它要求操作系统的内核打开一个文件,然后一点一点地从中读取字节。在 Ruby 中每行读取文件时,一次从文件中获取 512 个字节的数据,然后分成“行”。

      通过缓冲文件的内容,减少了 I/O 调用的数量,同时将文件划分为逻辑块。

      例子:

      将此类作为服务对象添加到您的应用中:

      class MyIO
        def initialize(filename)
          fd = IO.sysopen(filename)
          @io = IO.new(fd)
          @buffer = ""
        end
      
        def each(&block)
          @buffer << @io.sysread(512) until @buffer.include?($/)
      
          line, @buffer = @buffer.split($/, 2)
      
          block.call(line)
          each(&block)
        rescue EOFError
          @io.close
       end
      end
      

      调用它并传递:each 方法一个块:

      filename = './somewhere/large-file-4gb.txt'
      MyIO.new(filename).each{|x| puts x }
      

      在这篇详细的帖子中阅读它:

      Ruby Magic Slurping & Streaming Files By AppSignal

      【讨论】:

      • 注意:如果最后一行不以换行符结尾(至少在 Linux 中),该代码将忽略它。
      • 我认为在 "@io.close" 之前插入 "block.call(@buffer)" 会拾取丢失的不完整行。但是,我只玩过 Ruby 一天,所以我很可能是错的。它在我的应用程序中工作:)
      • 阅读了AppSignal的帖子后,这里似乎出现了一个小误会。您从该帖子中复制的执行缓冲 IO 的代码是 Ruby 实际使用 File.foreach 或 IO.foreach(它们是相同的方法)执行的示例实现。它们应该被使用,你不需要像这样重新实现它们。
      • @PeterH.Boling 大多数时候我也支持使用和不重新实现的心态。但是红宝石确实允许我们打开事物并毫无羞耻地戳它们的内部,这是它的好处之一。没有真正的“应该”或“不应该”,尤其是在 ruby​​/rails 中。只要你知道自己在做什么,并为它编写测试。
      【解决方案10】:

      如果文件很小(slurping):

      puts File.read("filename.txt")
      

      如果文件很大(流式传输):

      File.foreach("filename.txt") { |line| puts line }
      

      【讨论】:

        【解决方案11】:
        content = `cat file`
        

        我认为这种方法是最“不常见”的一种。也许这有点棘手,但如果安装了cat,它就可以工作。

        【讨论】:

        • 一个方便的技巧,但是调用 shell 有很多陷阱,包括 1) 命令在不同的操作系统上可能不同,2) 你可能需要在文件名中转义空格。你最好使用 Ruby 内置函数,例如content = File.read(filename)
        猜你喜欢
        • 1970-01-01
        • 2011-02-08
        • 2017-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-09
        • 2012-07-06
        相关资源
        最近更新 更多