我有2个日志文件,它们都包含以时间顺序从日期戳开始的文本行。我想将两个文件合并到一个文件中,该文件包含按时间顺序合并的两个文件中的所有行。由于日期戳的布局,在这种情况下按时间顺序排列与字母相同。
我编写了一个执行此操作的Ruby脚本并且工作正常,但我是Ruby新手只是学习语言而且我真正喜欢Ruby的一件事就是语法糖和代码的可读性。而且我不禁觉得我的解决方案非常笨重,而且肯定有更好的方法可以解决同样的问题。我并不需要在算法上更好地寻找,但在句法上。 Ruby似乎对几乎所有东西都有一个单线解决方案,所以也许是这样的问题。
if ARGV.length != 2
puts "Wrong number of arguments. Expected 2 arguments (path to 2 log files to be merged)"
end
merged_file = File.open("merge_out.txt", "w")
file1 = File.open(ARGV[0], "r")
file2 = File.open(ARGV[1], "r")
line1 = file1.gets
line2 = file2.gets
while (line1 != nil or line2 !=nil)
if line1 == nil
# no more line1 so write line2 and proceed file2
merged_file.puts line2
line2 = file2.gets
elsif line2 == nil
# no more line2 so write line1 and proceed file1
merged_file.puts line1
line1 = file1.gets
else
comp = line1<=>line2
#both lines present, write and proceed the (alphabetically) smaller one
#as this is the one with the earlier time stamp
if comp == -1
merged_file.puts line1
line1 = file1.gets
else
merged_file.puts line2
line2 = file2.gets
end
end
end
那么,如何更优雅地完成这项工作?
答案 0 :(得分:4)
有时添加维度会使解决方案更漂亮。从本质上讲,将file1,file2变量转换为数组[ file1, file2 ]
,这会打开很多Ruby Array
语法,执行您已编码到初始解决方案中的测试。
if ARGV.length < 2
puts "Wrong number of arguments. Expected 2 or more files to merge."
end
merged_file = File.open("merge_out.txt", "w")
files = ARGV.map { |filename| File.open( filename, "r") }
lines = files.map { |file| file.gets }
while lines.any?
next_line = lines.compact.min
file_id = lines.index( next_line )
merged_file.print next_line
lines[ file_id ] = files[ file_id ].gets
end
因此,这不仅更短,而且副作用可以同时处理更多输入文件。虽然如果你不需要,只需改回第一次检查。
答案 1 :(得分:3)
我知道这不是红宝石,但对于这类问题,一个简单的bash命令可以完成这项工作:
cat file1.dat file2.dat | sort > out.dat
如果你真的希望ruby用一小段代码完成工作:
File.open('out.dat','w') do |f|
f.puts (File.read('file1.dat') << File.read('file2.dat') ).split("\n").sort
end
请注意,这并未考虑您的数据已经部分排序的事实。它简洁,但不一定是最有效的方法。
答案 2 :(得分:2)
你可以将你的4个案例合并为两个案例:只要问问自己,line1是否是第一行:
if line1 && line1 < line2
…
答案 3 :(得分:2)
这不是ruby语法,但在算法上,您可以将每个文件读入数组或列表,然后对数组/列表进行排序,然后打印它。使用内置进行排序看起来更好看。根据内置的实现,它可能比您编写的任何代码都快。我想如果它成为一个问题你只会担心性能:)
在python中:
# create a list with all the first file in it as elements
with open(file1) as f:
content = f.readlines()
# add the second file contents to the list
with open(file1) as f
content = content + f.readlines() # list catenation
content.sort() # sort the list
# join all the lines in the list together into a string an print them
outfile.write(''.join(content))
由于你是一个红宝石新手,找到相同的红宝石内置将是一个很好的学习练习:)
请注意,如果文件很大,这不是一个好主意,因为它将整个文件吸入内存。再次 - 简单代码的代价。如果你有很多文件,你需要一些更笨重的代码来处理它们:)
编辑:
我用Google搜索了一些红宝石。 出现# google "read file into array ruby"
array = IO.readlines file1_pathname + IO.readlines file2_pathname
# google "sort array ruby
array.sort
# google "print array ruby"
puts array.inspect
是那种事。
HTH
答案 4 :(得分:1)
NeilSlater对我的另一个答案的评论使我的思绪继续“所以,如果你不能啜饮,那又怎样?”
这个怎么样:
line1 = file1.gets
line2 = file2.gets
while (line1 && line2)
while(line1.to_s >= line2.to_s) # to_s to protect against nil
merged_file.puts line2
line2 = file2.gets
end
while(line2.to_s > line1.to_s)
merged_file.puts line1
line1 = file1.gets
end
end
while(line1)
merged_file.puts line1
line1 = file1.gets
end
while(line2)
merged_file.puts line2
line2 = file2.gets
end
它不是很短,并没有使用任何语法魔法或方便的内置,但至少它更规则......