我想将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
干杯,
马丁
答案 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}