【问题标题】:FasterCSV: Check whether a file is invalid before accepting it - is there a simpler way?FasterCSV:在接受文件之前检查文件是否无效 - 有没有更简单的方法?
【发布时间】:2011-05-10 16:28:53
【问题描述】:

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

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

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

【问题讨论】:

  • 两个变化:1) 你需要def self.is_valid? 2) 删除多余的return 调用。发布为评论而不是答案,因为您基本上已经有了这个答案,我不想阻止其他答案。我建议您应该从问题中删除自己的代码,并在一段时间后将其作为答案发布。
  • 感谢您的 cmets!我已经修复了我的代码(当它不在方法的末尾时,我更喜欢明确地返回)
  • @egarcia begin; parse{ ... }; true; rescue; false; end 怎么样@
  • @Phrogz 作为一名红宝石爱好者,我是单行字的忠实粉丝。但是......请不要误会,但我真的不喜欢分号业务。在这种特殊情况下,我想我宁愿明确说明正在发生的事情,即使该方法跨越了几行。
  • @egarcia 要清楚,分号只是因为我不能在 cmets 中输入换行符。无论如何,只是对我认为更像 Ruby 的代码的建议。

标签: ruby-on-rails ruby csv fastercsv


【解决方案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

【讨论】:

    【解决方案2】:

    我昨天做了一些测试,结果发现我的解决方案不太奏效;在实现第一个 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,它接受一个块并逐行解析文件,但对我来说,这个简单的实现就足够了,它适用于所有情况。

    【讨论】:

    • 为什么不删除你之前的答案?
    【解决方案3】:

    我假设您想解析 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 通信并隐藏解析结果,因为我认为我的示例不是线程安全的 :)

    【讨论】:

    • 如果我正确阅读 FasterCSV 文档,如果你给它一个块,它不会 FasterCSV::parse 不会为解析结果分配任何内存(因此是{ |row| })。另一方面,至少在我的情况下,无效文件很早就被检测到了。通常在前 2 行。在这种特殊情况下,我实际上并不介意两次解析文件的速度损失。但是线程安全很重要。
    • 被否决,因为您的代码在类上设置了实例变量。如果您有两个 CSV 文件,验证第二个文件会破坏检查第一个文件有效性的能力。
    • @Phrogz 你是对的。我在最后一行中暗示了这一点。
    • @egarcia 我不怕内存使用,但是文件很大或很多时,你可能会注意到。
    猜你喜欢
    • 2011-12-19
    • 2011-06-03
    • 2021-04-25
    • 1970-01-01
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 2015-11-15
    相关资源
    最近更新 更多