arrays - 如何按段落或 block 将文件读入数组

标签 arrays ruby file io

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

block 1
some text
some text

block 2
some text
some text

如何将其读入数组?

最佳答案

这个问题经常被问到,我认为解释一下该怎么做会很有用,但首先我需要说一下:

不要尝试一口气读取文件。这称为“吞吐”,这是一个糟糕的主意,除非您可以保证您始终会读取小于 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"

这会让我不得不将它拆分成单独的行,试图找到空白行,然后将其分成 block 。而且,总是,天真的解决方案是使用正则表达式,这 kind-a 有效但不是最佳的。

或者我们可以尝试:

File.readlines('foo.txt')
#=> ["block 1\n", "some text\n", "some text\n", "\n", "block 2\n", "some text\n", "some text\n"]

然后还是要找空行,把数组转成子数组。

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

  1. 请记住之前关于 slurping 文件的警告,如果它是一个我们可以使用的小文件:

    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”。它实际上基于操作系统派生的全局值 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。如果文件尚未打开,或 each_line如果它是一个已经打开的文件。而且,由于您阅读了上面的链接,您已经知道我们为什么要使用逐行 IO。

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

    注意:我强烈怀疑使用 map那样会导致与吞噬文件类似的问题,因为 Ruby 会缓冲 foreach 的输出。在将其传递给 map 之前.相反,我们需要对 do 中读取的每个段落进行操作。 block :

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

    这样做对性能的影响很小,但因为目标是按段落或 block 处理,所以这是可以接受的。

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

关于arrays - 如何按段落或 block 将文件读入数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32955842/

相关文章:

ruby-on-rails - 在 rake 任务上运行 bash 命令之前运行服务器

c - 如何让多个进程同时读取同一个文件会减慢读取速度?

regex - 使用正则表达式查找多个文件扩展名的命令

c++ - 打印数组拷贝时出现段错误

PHP 在多维数组中查找值并获取对应的元素值

c++ - 为什么这个矩阵乘法代码不起作用

java - 从文件中读取特定页面

抛弃指向 const 的指针数组中的常量性,以及有关 C 的其他问题

ruby-on-rails - rails 5 : Preventing access to/admin pages based on IP address

ruby-on-rails - 检查可能是单个字符串或字符串数​​组的散列值的方法