每 n 行追加一行到文件 - ruby
Append line every n lines to file - ruby
我需要每 1000 行向文本文件添加一行。主要问题是文件大约有 3GB,所以我无法将整个文件加载到字符串或数组中。要处理大文件,我通常使用 File.foreach 但在这种情况下我找不到有关使用索引的任何信息。是否有任何其他选项可以解决此问题而无需将整个文件加载到内存?
这里有几个选项。
选项 1:
File.open('output.txt', 'w') do |outfile|
File.foreach('input.txt').each_with_index do |line, i|
outfile.puts(line)
outfile.puts '--- 1000 ---' if (i + 1) % 1000 == 0 && i != 0
end
end
这会在原始文件的每 1000 行之后插入行 '--- 1000 ---'
。它有一些缺点。主要是它必须检查每个索引并检查我们是否不在每一行的零行!但它有效。它适用于大文件而不会占用内存。
选项 2:
File.open('output.txt', 'w') do |outfile|
File.foreach('input.txt').each_slice(1000) do |lines|
outfile.puts(lines)
outfile.puts '--- 1000 ---'
end
end
此代码使用 Enumerable
的 each_slice
方法几乎完全相同。它每 1000 行生成一个数组,使用 puts
(接受 Array
s)将它们写出,然后在其后写入我们的标记行。然后重复接下来的 1000 行。不同之处在于,如果文件不是 1000 行的倍数,最后一次调用此块将生成一个小于 1000 行的数组,我们的代码仍将在其后附加我们的文本行。
我们可以通过测试数组的长度来解决这个问题,并且只在数组恰好为 1000 行时才写出我们的行。除了最后一个(给定一个不是 1000 行的倍数的文件)之外,每批 1000 行都是如此。
选项 2a:
File.open('output.txt', 'w') do |outfile|
File.foreach('input.txt').each_slice(1000) do |lines|
outfile.puts(lines)
outfile.puts '--- 1000 ---' unless lines.size < 1000
end
end
只有在将该行追加到文件末尾对您来说有问题时才需要进行此额外检查。否则,您可以忽略它以获得小的性能提升。
说到性能,下面是每个选项在包含 1,000,000 个 Lorem Ipsum 段落的 335.5 MB 文件上的表现。每个基准是处理整个文件 100 次的总时间。
选项 1:
103.859825 44.646519 148.506344 (152.286349)
[Finished in 152.6s]
选项 2:
96.249542 43.780160 140.029702 (145.210728)
[Finished in 145.7s]
选项 2a:
98.041073 45.788944 143.830017 (149.769698)
[Finished in 150.2s]
如您所见,选项 2 是最快的。请记住,选项 2/2a 理论上会使用更多内存,因为它一次加载 1000 行,但即便如此,它的上限也很小,因此处理大量文件应该不是问题。然而,它们都非常接近,我建议选择读起来最好或最有意义的任何选项。
希望对您有所帮助。
我需要每 1000 行向文本文件添加一行。主要问题是文件大约有 3GB,所以我无法将整个文件加载到字符串或数组中。要处理大文件,我通常使用 File.foreach 但在这种情况下我找不到有关使用索引的任何信息。是否有任何其他选项可以解决此问题而无需将整个文件加载到内存?
这里有几个选项。
选项 1:
File.open('output.txt', 'w') do |outfile|
File.foreach('input.txt').each_with_index do |line, i|
outfile.puts(line)
outfile.puts '--- 1000 ---' if (i + 1) % 1000 == 0 && i != 0
end
end
这会在原始文件的每 1000 行之后插入行 '--- 1000 ---'
。它有一些缺点。主要是它必须检查每个索引并检查我们是否不在每一行的零行!但它有效。它适用于大文件而不会占用内存。
选项 2:
File.open('output.txt', 'w') do |outfile|
File.foreach('input.txt').each_slice(1000) do |lines|
outfile.puts(lines)
outfile.puts '--- 1000 ---'
end
end
此代码使用 Enumerable
的 each_slice
方法几乎完全相同。它每 1000 行生成一个数组,使用 puts
(接受 Array
s)将它们写出,然后在其后写入我们的标记行。然后重复接下来的 1000 行。不同之处在于,如果文件不是 1000 行的倍数,最后一次调用此块将生成一个小于 1000 行的数组,我们的代码仍将在其后附加我们的文本行。
我们可以通过测试数组的长度来解决这个问题,并且只在数组恰好为 1000 行时才写出我们的行。除了最后一个(给定一个不是 1000 行的倍数的文件)之外,每批 1000 行都是如此。
选项 2a:
File.open('output.txt', 'w') do |outfile|
File.foreach('input.txt').each_slice(1000) do |lines|
outfile.puts(lines)
outfile.puts '--- 1000 ---' unless lines.size < 1000
end
end
只有在将该行追加到文件末尾对您来说有问题时才需要进行此额外检查。否则,您可以忽略它以获得小的性能提升。
说到性能,下面是每个选项在包含 1,000,000 个 Lorem Ipsum 段落的 335.5 MB 文件上的表现。每个基准是处理整个文件 100 次的总时间。
选项 1:
103.859825 44.646519 148.506344 (152.286349)
[Finished in 152.6s]
选项 2:
96.249542 43.780160 140.029702 (145.210728)
[Finished in 145.7s]
选项 2a:
98.041073 45.788944 143.830017 (149.769698)
[Finished in 150.2s]
如您所见,选项 2 是最快的。请记住,选项 2/2a 理论上会使用更多内存,因为它一次加载 1000 行,但即便如此,它的上限也很小,因此处理大量文件应该不是问题。然而,它们都非常接近,我建议选择读起来最好或最有意义的任何选项。
希望对您有所帮助。