您需要运行基准测试,使用 Ruby 的内置基准来确定您最快的选择。
但是,根据经验,我发现“啜饮”文件,即一次读取所有文件,并不比使用带有IO.foreach
orFile.foreach
的循环快。这是因为 Ruby 和底层操作系统会在读取发生时进行文件缓冲,从而允许您的循环从内存发生,而不是直接从磁盘发生。foreach
不会像那样为您剥离行终止符split
,因此您需要添加 achomp
或者chomp!
如果您想改变读入的行:
File.foreach('/path/to/file') do |li|
puts li.chomp
end
或者
File.foreach('/path/to/file') do |li|
li.chomp!
puts li
end
此外,slurping 存在不可扩展的问题。您最终可能会尝试读取比内存更大的文件,从而使您的机器陷入瘫痪,而逐行读取则永远不会这样做。
以下是一些性能数据:
#!/usr/bin/env ruby
require 'benchmark'
require 'fileutils'
FILENAME = 'test.txt'
LOOPS = 1
puts "Ruby Version: #{RUBY_VERSION}"
puts "Filesize being read: #{File.size(FILENAME)}"
puts "Lines in file: #{`wc -l #{FILENAME}`.split.first}"
Benchmark.bm(20) do |x|
x.report('read.split') { LOOPS.times { File.read(FILENAME).split("\n") }}
x.report('read.lines.chomp') { LOOPS.times { File.read(FILENAME).lines.map(&:chomp) }}
x.report('readlines.map.chomp1') { LOOPS.times { File.readlines(FILENAME).map(&:chomp) }}
x.report('readlines.map.chomp2') { LOOPS.times { File.readlines(FILENAME).map{ |s| s.chomp } }}
x.report('foreach.map.chomp1') { LOOPS.times { File.foreach(FILENAME).map(&:chomp) }}
x.report('foreach.map.chomp2') { LOOPS.times { File.foreach(FILENAME).map{ |s| s.chomp } }}
end
结果:
Ruby Version: 1.9.3
Filesize being read: 42026131
Lines in file: 465440
user system total real
read.split 0.150000 0.060000 0.210000 ( 0.213365)
read.lines.chomp 0.470000 0.070000 0.540000 ( 0.541266)
readlines.map.chomp1 0.450000 0.090000 0.540000 ( 0.535465)
readlines.map.chomp2 0.550000 0.060000 0.610000 ( 0.616674)
foreach.map.chomp1 0.580000 0.060000 0.640000 ( 0.641563)
foreach.map.chomp2 0.620000 0.050000 0.670000 ( 0.662912)
在今天的机器上,一个 42MB 的文件可以很安全地读入 RAM。我看到的文件比我们的一些生产主机的内存大得多。虽然foreach
速度较慢,但如果没有足够的内存,它也不会通过吸收所有内存来让机器瘫痪。
在 Ruby 1.9.3 上,使用该map(&:chomp)
方法而不是旧形式的map { |s| s.chomp }
,要快得多。旧版本的 Ruby 并非如此,因此请谨慎购买。
另外,请注意,在我几年前的 Mac Pro 上,上述所有操作都在不到一秒的时间内处理了数据。总而言之,我会说担心加载速度是过早的优化,真正的问题是加载数据后要做什么。