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

时间:2011-05-27 09:20:34

标签: ruby file split size

我想将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

干杯,

马丁

4 个答案:

答案 0 :(得分:16)

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

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)!

块文件的数字将通过chunksize从当前文件光标位置的整数除法生成,格式为“%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|
        line = ""
        while fh_out.size <= (chunksize-line.length) && !fh_in.eof?
          line = fh_in.readline
          fh_out << line
        end
      end
      outfilenum += 1
    end
  end
end

我必须引入辅助变量line,因为我想确保粗块文件大小始终低于 chunksize限制!如果您不进行此扩展检查,您将获得超出限制的文件大小。 while语句仅在已经写入行时成功检查下一个迭代步骤。 (使用.ungetc或其他复杂计算将使代码更难以读取并且不会短于此示例。)

不幸的是,您必须进行第二次EOF检查,因为最后一次块重复将主要导致较小的块。

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

答案 1 :(得分:11)

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

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

选项包括:

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

除非您使用的是Windows,否则就可以了。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

#!/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}