【问题标题】:Enumerator returning each line of a file, but chomped枚举器返回文件的每一行,但 chomped
【发布时间】:2016-08-20 02:20:44
【问题描述】:

我想编写一个返回枚举器的方法(类似于File::foreach 返回的那个),它连续返回文件的每一行,但是被chomped 了。我的第一种方法是

def chomped_lines(filename)
  File.foreach(filename).map(&:chomp).each
end

并将其用作

my_enum = chomped_lines('my_file.txt')
....
my_enum.each { |line| .... }

我想,Enumerator::lazy 可以在这里以某种方式使用,但无论如何,代码应该在 Ruby 1.9.3 上运行,这意味着我还没有惰性枚举器。

编写这样一个枚举器的好方法是什么?

这似乎行得通,但是如果我理解这一点是正确的(如果我错了请纠正我),文件将作为一个整体进入内存,以便应用map

【问题讨论】:

    标签: ruby


    【解决方案1】:

    我编写了一个类,我相信它可以满足您的要求。它也作为要点发布在https://gist.github.com/keithrbennett/9f126fa17d9df5e3aacaf638b198dfb9

    在类定义之后有一些代码以几种方式练习该类。

    #!/usr/bin/env ruby
    
    class ChompedFileLines
      include Enumerable
    
      def initialize(filespec)
        @filespec = filespec
      end
    
      def each
          File.foreach(@filespec) { |line| yield line.chomp }
      end
    end
    
    # Exercise the class:
    
    FILESPEC = '/usr/share/dict/words'
    
    chomped_file_lines = ChompedFileLines.new(FILESPEC)
    puts; p chomped_file_lines; puts
    chomped_file_lines.first(10).each { |line| p line }
    
    chomped_file_lines_enum = ChompedFileLines.new(FILESPEC).to_enum
    puts; p chomped_file_lines_enum; puts
    10.times { p chomped_file_lines_enum.next }
    

    【讨论】:

    • 确实,这似乎工作得很好,虽然我个人不太喜欢我必须为此创建一个新类的事实。 @CarySwoveland 提出的解决方案避免了这个问题。最后,这似乎是一个品味问题。
    • 虽然创建一个新类似乎很重要,但该类非常简短。这可能是一个品味问题,但对我来说,这个类以一种更容易理解的方式组织代码。此外,这种方法提供了更高的内聚性/更低的耦合性,因为枚举机制和枚举期间执行的行为是分开的(尽管公平地说,这也可以在 Cary 的代码中通过以代码块的形式参数化行为来实现.)
    【解决方案2】:

    您可以按如下方式创建枚举器。首先,让我们创建一个演示文件。

    str =<<_
    Now is the
    time for all
    Rubiests to
    come to the
    aid of their
    bowling team.
    _
    
    fname = "temp"
    File.write(fname, str)
      #=> 75
    

    IO#foreach 没有块返回一个枚举器:

    efe = File.foreach(fname)
      #=> #<Enumerator: File:foreach("temp")> 
    

    所以我们只需要将此枚举器嵌入另一个从一行中剔除换行符的枚举器中:

    echomp = Enumerator.new do |y|
      loop do
        y << efe.next.chomp
      end
    end
      #=> #<Enumerator: #<Enumerator::Generator:0x007fbe128837b8>:each> 
    

    让我们试试吧:

    echomp.next
      #=> "Now is the" 
    echomp.next
      #=> "time for all" 
    echomp.next
      #=> "Rubiests to" 
    echomp.next
      #=> "come to the" 
    echomp.next
      #=> "aid of their" 
    echomp.next
      #=> "bowling team." 
    echomp.next
      #=> StopIteration: iteration reached an end
    

    您当然可以将其包装在一个方法中:

    def foreach_with_chomp(fname)
      efe = File.foreach(fname)
      Enumerator.new do |y|
        loop do
          y << efe.next.chomp
        end
      end
    end
    
    foreach_with_chomp(fname).each { |s| print "#{s} " }
    Now is the time for all Rubiests to come to the aid of their bowling team. 
    

    【讨论】:

    • 如果我正确理解了您的方法,通过将foreach 枚举器 (efe.next) 放入新枚举器的循环中,您可以避免将整个文件放入内存中。这是正确的吗?
    猜你喜欢
    • 1970-01-01
    • 2013-10-17
    • 2012-05-23
    • 1970-01-01
    • 2015-11-28
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2013-06-18
    相关资源
    最近更新 更多