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

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

block 1 some text some text block 2 some text some text 

如何将其读入数组?

这经常被问到我认为解释做什么是有用的,但首先我需要这样说:

不要试图一口气读取文件。 这被称为“啜饮”,并且是一个坏主意,除非你能保证你总是会得到大小不到1MB的文件。 有关更多信息,请参阅“ 为什么”啜饮“文件不是一个好的做法? ”。

如果我有一个看起来像这样的文件:

 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" 。 这就是“行分隔符”,对于* nix类型的操作系统通常定义为“\ n”,对于Windows定义为“\ r \ n”。 它实际上是基于OS派生的全局值Ruby集合亲切地称为$/$RS$INPUT_RECORD_SEPARATOR 。 它们记录在英语模块中。 记录分隔符是文本文件中用于分隔两行的字符,或者,就我们的目的而言,由两个行尾字符分隔的一组行,或者换句话说,一个段落。

    阅读后,可以轻松清理内容以删除尾随行尾:

     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,如果文件已经打开,我们可以使用each_line 。 而且,由于您阅读了上面的链接,您已经知道我们为什么要使用逐行IO。

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

    注意:我强烈怀疑使用这样的map会导致类似于诽谤文件的问题,因为Ruby会在传递给map之前缓冲foreach的输出。 相反,我们需要对do块中读取的每个段落进行do

     File.foreach('foo.txt', "\n\n") do |ary| ary.rstrip.split("\n").each do |line| # do something with the individual line end end 

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

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