如何通过段落或块将文件读入数组

时间:2015-10-05 18:56:32

标签: arrays ruby file io

我有一个文件,其中包含由空行分隔的文本块,如下所示:

block 1
some text
some text

block 2
some text
some text

如何将其读入数组?

1 个答案:

答案 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"]

然后仍然必须找到空白行并将数组转换为子数组。

相反,有两种简单的方法可以加载文件。

  1. 请记住先前关于诽谤文件的警告,如果它是我们可以使用的小文件:

    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
    
  2. 如果它是一个大文件,如果文件尚未打开,我们可以通过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

    这样做在性能上很小,但是,因为目标是按段落或块进行处理,所以它是可以接受的。

  3. 另请注意,这是一个社区Wiki,因此请进行适当的编辑和贡献。