Ruby:如何将一个文件拆分为多个给定大小的文件



我想将一个txt文件拆分为多个文件,其中每个文件包含的大小不超过5Mb。我知道有工具可以做到这一点,但我需要这一点作为一个项目,并想用Ruby来做。此外,如果可能的话,我更喜欢在块上下文中使用File.open来做这件事,但我失败得很惨:o(

#!/usr/bin/env ruby
require 'pp'
MAX_BYTES = 5_000_000
file_num = 0
bytes    = 0
File.open("test.txt", 'r') do |data_in|
  File.open("#{file_num}.txt", 'w') do |data_out|
    data_in.each_line do |line|
      data_out.puts line
      bytes += line.length
      if bytes > MAX_BYTES
        bytes = 0
        file_num += 1
        # next file
      end
    end
  end
end

这件作品,但我认为它不优雅。此外,我仍然想知道它是否可以在块上下文中使用File.open来完成。

#!/usr/bin/env ruby
require 'pp'
MAX_BYTES = 5_000_000
file_num = 0
bytes    = 0
File.open("test.txt", 'r') do |data_in|
  data_out = File.open("#{file_num}.txt", 'w')
  data_in.each_line do |line|
    data_out = File.open("#{file_num}.txt", 'w') unless data_out.respond_to? :write
    data_out.puts line
    bytes += line.length
    if bytes > MAX_BYTES
      bytes = 0
      file_num += 1
      data_out.close
    end
  end
  data_out.close if data_out.respond_to? :close
end

干杯,

Martin

[更新]编写一个没有任何辅助变量的短版本,并将所有内容放入一个方法中:

def chunker f_in, out_pref, chunksize = 1_073_741_824
  File.open(f_in,"r") do |fh_in|
    until fh_in.eof?
      File.open("#{out_pref}_#{"%05d"%(fh_in.pos/chunksize)}.txt","w") do |fh_out|
        fh_out << fh_in.read(chunksize)
      end
    end
  end
end
chunker "inputfile.txt", "output_prefix" (, desired_chunk_size)

您可以使用.read(length),而不是循环行,只对EOF标记和文件光标执行循环。

这样可以确保大块文件永远不会大于您想要的块大小。

另一方面,它从不关心换行(n)!

区块文件的数字将由当前文件游标位置除以区块大小的整数生成,格式为"%"05d";其产生具有前导零的5位数(00001)。

这是可能的,因为使用了.read(chunksize)。在下面的第二个示例中,它无法使用!

更新:换行识别拆分

如果你真的需要n的完整行,那么使用这个修改后的代码片段:

def chunker f_in, out_pref, chunksize = 1_073_741_824
  outfilenum = 1
  File.open(f_in,"r") do |fh_in|
    until fh_in.eof?
      File.open("#{out_pref}_#{outfilenum}.txt","w") do |fh_out|
        loop do
          line = fh_in.readline
          fh_out << line
          break if fh_out.size > (chunksize-line.length) || fh_in.eof?
        end
      end
      outfilenum += 1
    end
  end
end

我不得不引入一个辅助变量line,因为我想确保块文件大小始终低于chunksize的限制!如果不进行此扩展检查,文件大小也会超过限制。while语句只有在行已经写入时才能在下一个迭代步骤中成功检查。(使用.ungetc或其他复杂计算会使代码更不可读,并且不会比本例更短。)

不幸的是,您必须进行第二次EOF检查,因为最后一次块迭代将主要产生较小的块。

还需要两个辅助变量:如上所述的line,需要outfilenum,因为生成的文件大小大多与精确的chunksize不匹配。

对于任何大小的文件,即使考虑到启动单独可执行文件的成本,split也将比从头构建的Ruby代码更快。它也是您不必编写、调试或维护的代码:

system("split -C 1M -d test.txt ''")

选项包括:

  • -C 1M在每个区块中放入总计不超过1M的行
  • -d在输出文件名中使用十进制后缀
  • test.txt输入文件的名称
  • ''使用空白输出文件前缀

除非你在Windows上,否则这就是你的选择。

不要在infile块中打开outfile,而是打开文件并将其分配给变量。当达到文件大小限制时,请关闭该文件并打开一个新文件。

这段代码实际上很有效,它很简单,并且使用了数组,这使它更快:

#!/usr/bin/env ruby
data = Array.new()
MAX_BYTES = 3500
MAX_LINES = 32
lineNum = 0
file_num = 0
bytes    = 0

filename = 'W:/IN/tangoZ.txt_100.TXT'
r = File.exist?(filename)
puts 'File exists =' + r.to_s + ' ' +  filename
file=File.open(filename,"r")
line_count = file.readlines.size
file_size = File.size(filename).to_f / 1024000
puts 'Total lines=' + line_count.to_s + '   size=' + file_size.to_s + ' Mb'
puts ' '

file = File.open(filename,"r")
#puts '1 File open read ' + filename
file.each{|line|          
     bytes += line.length
     lineNum += 1
     data << line    
        if bytes > MAX_BYTES  then
       # if lineNum > MAX_LINES  then     
              bytes = 0
              file_num += 1
          #puts '_2 File open write ' + file_num.to_s + '  lines ' + lineNum.to_s
             File.open("#{file_num}.txt", 'w') {|f| f.write data.join}
         data.clear
         lineNum = 0
        end

}
## write leftovers
file_num += 1
#puts '__3 File open write FINAL' + file_num.to_s + '  lines ' + lineNum.to_s
File.open("#{file_num}.txt", 'w') {|f| f.write data.join}

相关内容

  • 没有找到相关文章

最新更新