我有一个文件,其中包含由空行分隔的文本块,如下所示:
block 1
some text
some text
block 2
some text
some text
如何将其读入数组?
答案 0 :(得分:3)
经常被问到我认为解释做什么有用,但首先我需要这样说:
不要尝试一口气读取文件。这被称为"啜饮",除非你能保证这一点,否则这是一个坏主意您始终可以获得大小小于1MB的文件。见" Why is "slurping" a file not a good practice?"了解更多信息。
如果我有一个类似的文件:
block 1
some text
some text
block 2
some text
some text
我尝试正常阅读,我会得到类似的内容:
File.read('foo.txt')
#=> "block 1\nsome text\nsome text\n\nblock 2\nsome text\nsome text\n"
这将让我不得不将其拆分为单独的行,试图找到空行,然后将其分成块。并且,总是天真的解决方案是使用正则表达式,这种方式可行,但它不是最佳的。
或者我们可以试试:
File.readlines('foo.txt')
#=> ["block 1\n", "some text\n", "some text\n", "\n", "block 2\n", "some text\n", "some text\n"]
然后仍然必须找到空白行并将数组转换为子数组。
相反,有两种简单的方法可以加载文件。
请记住先前关于诽谤文件的警告,如果它是我们可以使用的小文件:
File.readlines('foo.txt', "\n\n")
#=> ["block 1\nsome text\nsome text\n\n", "block 2\nsome text\nsome text\n"]
请注意在第二个参数中使用"\n\n"
。那是"行分隔符",通常被定义为" \ n"对于* nix型操作系统和" \ r \ n"对于Windows。它实际上是基于操作系统派生的全局值Ruby集合亲切地称为$/
,$RS
或$INPUT_RECORD_SEPARATOR
。它们已记录在English模块中。记录分隔符是文本文件中用于分隔两行的字符,或者,就我们的目的而言,由两个行尾字符分隔的一组行,或者换句话说,一个段落。
阅读后,可以轻松清理内容以删除尾随的行尾:
File.readlines('foo.txt', "\n\n").map(&:rstrip)
#=> ["block 1\nsome text\nsome text", "block 2\nsome text\nsome text"]
或者将它们分成子数组:
File.readlines('foo.txt', "\n\n").map{ |s| s.rstrip.split("\n") }
#=> [["block 1", "some text", "some text"], ["block 2", "some text", "some text"]]
所有示例都可以使用类似于以下的段落:
File.readlines('foo.txt', "\n\n").map(&:rstrip).each do |line|
# do something with line
end
或:
File.readlines('foo.txt', "\n\n").map{ |s| s.rstrip.split("\n") }.each do |paragraph|
# do something with the sub-array `paragraph`
end
如果它是一个大文件,如果文件尚未打开,我们可以通过foreach
使用Ruby的逐行IO,或{{1如果它是一个已打开的文件。而且,由于您阅读了上述链接,您已经知道我们为什么要使用逐行IO。
each_line
File.foreach('foo.txt', "\n\n") #=> #<Enumerator: File:foreach("foo.txt", "\n\n")>
会返回一个枚举器,因此我们需要使用foreach
来读取数组,以便我们可以看到结果,但通常我们不必这样做:
to_a
它很容易使用File.foreach('foo.txt', "\n\n").to_a
#=> ["block 1\nsome text\nsome text\n\n", "block 2\nsome text\nsome text\n"]
,如上所述:
foreach
注意:我强烈怀疑使用File.foreach('foo.txt', "\n\n").map(&:rstrip)
#=> ["block 1\nsome text\nsome text", "block 2\nsome text\nsome text"]
File.foreach('foo.txt', "\n\n").map(&:rstrip).map{ |s| s.rstrip.split("\n") }
#=> [["block 1", "some text", "some text"], ["block 2", "some text", "some text"]]
这样会导致与文件相似的问题,因为Ruby会在将map
输出传递给foreach
之前对其进行缓冲。相反,我们需要对map
块中读取的每个段落进行操作:
do
这样做在性能上很小,但是,因为目标是按段落或块进行处理,所以它是可以接受的。
另请注意,这是一个社区Wiki,因此请进行适当的编辑和贡献。