找出CSV文件中是否包含空字段?

时间:2014-03-31 16:04:32

标签: ruby csv

使用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万个字段的文件时,这很慢,我有很多。有没有更快更优雅的方法呢?

3 个答案:

答案 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行。您将看到运行时间没有变化。这是因为大部分时间都花在阅读和解析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命令的修改将是最快的。