FasterCSV:在接受之前检查文件是否无效 - 是否有更简单的方法?

时间:2011-05-10 16:28:54

标签: ruby-on-rails ruby csv fastercsv

我在Ruby on Rails应用程序上使用FasterCSV,如果文件无效,它当前会抛出异常。

我查看了FasterCSV doc,似乎如果我将FasterCSV::parse与一个块一起使用,它会一次读取一行文件,而不会分配太多内存。如果文件中存在任何类型的错误,它将抛出FasterCSV::MalformedCSV异常。

我已经实现了自定义解决方案,但我不确定它是最好的解决方案(请参阅下面的答案)。我有兴趣了解替代品

3 个答案:

答案 0 :(得分:1)

这是我目前的解决方案。我真的很想知道改进/替代方案。

# /lib/fastercsv_is_valid.rb

class FasterCSV

  def self.is_valid?(file, options = {})
    begin
      FasterCSV.parse(file, options) { |row| }
      true
    rescue FasterCSV::MalformedCSV
      false
    end
  end

end

我使用这样的方法:

# /models/csv_importer.rb

class CsvImporter
  include ActiveRecord::Validations

  validates_presence_of :file
  validate check_file_format

...

  private

  def check_file_format
    errors.add :file, "Malformed CSV! Please check syntax" unless FasterCSV::is_valid? file
  end
end

答案 1 :(得分:0)

我昨天做了一些测试,结果证明我的解决方案不太有效;在实现第一个is_valid后,我一直在有效的CSV上获取空数组。我不确定它是否是一个FasterCSV缓存问题或我的代码中的某些内容,我不知道它是否与我的测试设置有关,但我决定改为实现safe_parse

#/lib/faster_csv_safe_parse.rb
class FasterCSV

  def self.safe_parse(file, options = {})
    begin
      FasterCSV.parse(file, options)
    rescue FasterCSV::MalformedCSVError
      nil
    end
  end

end

如果文件有效,则返回解析后的数组,否则返回nil。然后我可以按如下方式实现我的验证:

# /models/csv_importer.rb

class CsvImporter
  include ActiveRecord::Validations

  validates_presence_of :file
  validate check_file_format
  attr_accessor csv_data

  def csv_data
    @csv_data ||= FasterCSV.safe_parse(file)
  end

...

  private

  def check_file_format
    errors.add :file, "Malformed CSV! Please check syntax" if csv_data.nil?
  end
end

我想有可能实现一个接受块的safe_parse并逐行解析文件,但就我的目的而言,这个简单的实现就足够了,并且它适用于所有情况。

答案 2 :(得分:-1)

我假设您要解析CSV并对解析后的结果执行某些操作。最糟糕的情况是您的CSV有效并且您再次解析该文件。我会写这样的东西来隐藏解析后的结果,所以你只需要解析一次CSV:

module FasterCSV

  def self.parse_and_validate(file, options = {})

    begin
      @parsed_result = FasterCSV.parse(file, options) { |row| }
    rescue FasterCSV::MalformedCSV
      @invalid = true
    end
  end

  def self.is_valid?
    !@invalid
  end    

  def self.parsed_result
    @parsed_result if self.valid?
  end

end

然后:

class CsvImporter
  include ActiveRecord::Validations

  validates_presence_of :file
  validate check_file_format

  # I assume you use the parsed result after the validations so in a before_save or something
  def do_your_parse_stuff
    here you would use FasterCSV::parsed_result
  end
...

  private

  def check_file_format
    FasterCSV::parse_and_validate(file)
    errors.add :file, "Malformed CSV! Please check syntax" unless FasterCSV::is_valid?
  end
end

在上面的例子中,您可能希望将内容移动到另一个类中,该类负责与FasterCSV进行通信并隐藏已解析的结果,因为我不认为我的示例是线程安全的:)