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


280

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

例如,这是一种方法:

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

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


6
我认为目前的胜出答案不正确。
歌手

Answers:


259
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

14
这几乎不是惯用的Ruby。使用foreach而不是并不open需要该each_line块。
Tin Man

7
f.each { |line| ... }并且f.each_line { |line| ... }似乎具有相同的行为(至少在Ruby 2.0.0中)。
chbrown 2015年

327

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

puts File.read(file_name)

实际上,IO.read或者File.read自动关闭文件,因此不需要File.open与块一起使用。


16
IO.readFile.read自动关闭文件,尽管您的措辞听起来像他们没有。
Phrogz 2011年

15
他已经说过“如果文件不太长”。非常适合我的情况。
jayP 2015年

227

警惕“含糊”文件。那是您一次将整个文件读入内存的时候。

问题在于它的伸缩性不好。您可能正在开发具有合理大小的文件的代码,然后将其投入生产,突然发现您正在尝试读取以GB为单位的文件,并且主机在尝试读取和分配内存时处于冻结状态。

逐行I / O速度非常快,并且几乎总是像制浆一样有效。实际上,它出奇地快。

我喜欢使用:

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

要么

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

文件继承自IO,并且foreach位于IO中,因此您可以使用其中任何一个。

我有一些基准测试,它显示了read为什么“吸引”文件不是一个好习惯? ” 尝试通过vs.逐行I / O 读取大文件的影响。


6
这正是我想要的。我有一个500万行的文件,真的不希望将其加载到内存中。
Scotty C.

68

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

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

对于二进制文件,您可以指定一个nil-separator和一个块大小,如下所示:

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


2
for_each文件或IO中没有任何内容。使用foreach代替。
Tin Man

1
当在此处记录要在答案中使用的代码时,我通常使用Sublime Text编辑器和RubyMarkers插件。类似于使用IRB,它使显示中间结果变得非常容易。Sublime Text 2的Seeing Is Believing插件功能非常强大。
Tin Man

1
好答案。对于最后一个示例,我可能建议使用 while而不是loopensure来确保即使引发异常也可以关闭文件。像这样(更换分号用换行)begin; f = File.open('testfile'); while line = f.gets; puts line; end; ensure; f.close; end
antinome

1
是的,@ antinome更好,改善了答案。谢谢!
维克多·克洛斯

26

一种简单的方法是使用readlines

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

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


5
read任何变体一样,这会将整个文件拉入内存,如果文件大于可用内存,则可能导致严重问题。另外,由于它是一个数组,因此Ruby必须创建该数组,这会进一步减慢该过程。
锡人


9

我通常这样做:

open(path_in_string, &:read)

这将为您提供整个文本作为字符串对象。它仅在Ruby 1.9下有效。


这很好又简短!它也关闭文件吗?
mrgreenfur

5
它会关闭它,但它不可伸缩,因此请小心。
Tin Man


1

一种更有效的方法是通过要求操作系统的内核打开文件,然后一点一点地从文件中读取字节来进行流传输。在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通过AppSignal进行文件流处理


请注意:如果代码没有以换行符结尾(至少在Linux中是这样),那么该代码将忽略最后一行。
约尔根

我认为在“ @ io.close”之前插入“ block.call(@buffer)”将弥补丢失的不完整行。但是,我只和Ruby玩过一天,所以我很可能错了。它在我的应用程序中有效:)
Jorgen

阅读AppSignal帖子后,似乎这里有些误会了。您从那篇做缓冲IO的帖子中复制的代码是Ruby实际对File.foreach或IO.foreach(相同方法)所做的示例实现。应该使用它们,并且您不需要像这样重新实现它们。
Peter H. Boling

@ PeterH.Boling我大多数时候也都喜欢使用和不要重新实现。但是红宝石的确使我们能够打开事物并戳入它们的内部而不会感到羞耻,这是它的特权之一。没有真正的“应该”或“不应该”,特别是在红宝石/铁轨中。只要您知道自己在做什么,并为此编写测试。
Khalil Gharbaoui

0
content = `cat file`

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


1
这是一个方便的技巧,但是调用shell会有很多陷阱,包括1)命令在不同的OS上可能不同,2)您可能需要在文件名中转义空格。使用Ruby内置函数会更好,例如content = File.read(filename)
Jeff Ward
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.