使用Ruby 1.9.3,我想读取带有标题的CSV文件并扫描每个字段以查看它是否为空且不包含值,如foo,,bar,foofoo,barbar
(第二个)。
我的方法如下:
require 'CSV'
#read csv file line by line
CSV.foreach(filename,headers:true) do |row|
#loop through each element within the current row
for i in (0..row.length-1)
#check for empty fields
if !row[i]
puts "empty field"
end
end
end
嗯,这很有效,但是当处理一个包含大约1800万个字段的文件时,这很慢,我有很多。有没有更快更优雅的方法呢?
答案 0 :(得分:4)
grep
编辑:拥有我的大文件我还使用grep
测试了Uri Agassi的aproach,以获取包含空字段的文件行:
File.new(filename).grep(/(^,|,(,|$))/)
它快了大约10倍。如果您需要访问字段,可以使用CSV.parse
:
require 'csv'
File.new("/tmp/big.csv").grep(/(^,|,(,|%))/).each do |row_string|
CSV.parse(row_string) do |row|
puts row[1]
end
end
否则,如果你必须解析整个CSV文件,答案很可能是否定的。尝试在没有检查部分的情况下运行脚本 - 只需读取CSV行。您将看到运行时间没有变化。这是因为大部分时间都花在阅读和解析CSV文件上。
您可能想知道Ruby是否有更快的CSV库。确实存在一个名为FasterCSV
的gem,但Ruby 1.9已将其作为内置的CSV库使用,因此使用Ruby可能不会更快。
有一个名为excelsior的红宝石宝石,它使用原生的CSV解析器。您可以通过gem install excelsior
安装它,并按照以下方式使用它:
require 'excelsior'
Excelsior::Reader.rows(File.open('/tmp/big.csv')) do |row|
row.each do |column|
unless column
puts "empty field"
end
end
end
我使用像你这样的文件(72M,~30k条目,2.5k字段)测试了这段代码,它的速度大约是其两倍,但是几行之后会出现段错误,因此宝石可能不稳定。
CSV
正如您在评论中提到的,还有一些惯用的方法可以解决此问题,例如使用each
代替for
循环或使用unless
代替{{1使用two spaces for indentation,并将其转换为:
if !
这不会提高速度。
答案 1 :(得分:1)
解析CSV
可能需要占用大量CPU。如果您想要的是获取包含空字段的行(即包含,,
以,
开头或以,
结尾),则可以使用grep
文件的原始行,而不实际解析它们:
File.new(filename).grep(/(^,|,(,|$))/)
# => all the lines which have an empty field
我担心你仍然会查看所有文件并阅读它们,所以它可能没有你希望的那么快,但除非文件上有一些索引,否则我不能看看周围的方式。
答案 2 :(得分:1)
您可以使用Enumerable#any?
CSV.foreach(filename,headers:true) do |row|
puts "empty field" if row.any?(&:nil?)
end
我认为grep
解决方案仍然会更快。对linux grep
命令的修改将是最快的。