【问题标题】:Read a file in chunks in Ruby在 Ruby 中分块读取文件
【发布时间】:2021-08-24 07:29:20
【问题描述】:

我需要以 MB 块读取文件,在 Ruby 中是否有更简洁的方法来执行此操作:

FILENAME="d:\\tmp\\file.bin"
MEGABYTE = 1024*1024
size = File.size(FILENAME)
open(FILENAME, "rb") do |io| 
  read = 0
  while read < size
    left = (size - read)
    cur = left < MEGABYTE ? left : MEGABYTE
    data = io.read(cur)
    read += data.size
    puts "READ #{cur} bytes" #yield data
  end
end

【问题讨论】:

    标签: ruby


    【解决方案1】:

    改编自 Ruby Cookbook 第 204 页:

    FILENAME = "d:\\tmp\\file.bin"
    MEGABYTE = 1024 * 1024
    
    class File
      def each_chunk(chunk_size = MEGABYTE)
        yield read(chunk_size) until eof?
      end
    end
    
    open(FILENAME, "rb") do |f|
      f.each_chunk { |chunk| puts chunk }
    end
    

    免责声明:我是 ruby​​ 新手,尚未对此进行测试。

    【讨论】:

    • 是的,这行得通。但是,我认为如果剩下的字节数小于块大小, IO.read 会抛出。我认为是因为我读过 IO.readbyte,它会抛出 TruncatedDataError。看起来这不适用于阅读。对我来说是一个oversite。谢谢!
    【解决方案2】:

    或者,如果您不想猴子补丁File

    until my_file.eof?
      do_something_with( my_file.read( bytes ) )
    end
    

    例如,将上传的临时文件流式传输到新文件中:

    # tempfile is a File instance
    File.open( new_file, 'wb' ) do |f|
      # Read in small 65k chunks to limit memory usage
      f.write(tempfile.read(2**16)) until tempfile.eof?
    end
    

    【讨论】:

    • 快速问:为什么2**16?我看到其他人使用2**2010241024*1024。你有很多代表,所以我想我会问你。 :)
    【解决方案3】:

    您可以使用IO#each(sep, limit),并将sep设置为nil或空字符串,例如:

    chunk_size = 1024
    File.open('/path/to/file.txt').each(nil, chunk_size) do |chunk|
      puts chunk
    end
    

    【讨论】:

    • @EricDuminil 感谢您提醒我,我忘记了 sep 参数。它现在应该分块读取文件。
    • eachsep == nilsep == ’’ 都为我返回了可变长度的块。
    【解决方案4】:

    如果您查看 ruby​​ 文档: http://ruby-doc.org/core-2.2.2/IO.html 有一行是这样的:

    IO.foreach("testfile") {|x| print "GOT ", x }
    

    唯一的警告是。因为,这个进程读取临时文件的速度比 生成的流,IMO,应抛出延迟。

    IO.foreach("/tmp/streamfile") {|line|
      ParseLine.parse(line)
      sleep 0.3 #pause as this process will discontine if it doesn't allow some buffering 
    }
    

    【讨论】:

      【解决方案5】:

      https://ruby-doc.org/core-3.0.2/IO.html#method-i-read 给出了一个使用read(length) 迭代固定长度记录的示例:

      # iterate over fixed length records
      open("fixed-record-file") do |f|
        while record = f.read(256)
          # ...
        end
      end
      

      如果 length 是一个正整数,read 会尝试读取 length 个字节而不进行任何转换(二进制模式)。如果在读取任何内容之前遇到 EOF,它将返回 nil。如果在读取期间遇到 EOF,则返回少于 length 个字节。对于整数 length,结果字符串始终采用 ASCII-8BIT 编码。

      【讨论】:

        【解决方案6】:
        FILENAME="d:/tmp/file.bin"
        
        class File
          MEGABYTE = 1024*1024
        
          def each_chunk(chunk_size=MEGABYTE)
            yield self.read(chunk_size) until self.eof?
          end
        end
        
        open(FILENAME, "rb") do |f|
          f.each_chunk {|chunk| puts chunk }
        end
        

        它有效,mbarkau。为了清楚起见,我只是将常量定义移至 File 类并添加了几个“self”。

        【讨论】:

        • 我不会使用额外的常量 MEGABYTE,而是:def each_chunk(chunk_size=2**20)
        猜你喜欢
        • 1970-01-01
        • 2017-01-13
        • 1970-01-01
        • 1970-01-01
        • 2015-12-30
        • 2019-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多