我在 Ruby on Rails 应用程序上使用 FasterCSV,目前如果文件无效,它会引发异常。
我查看了FasterCSV 文档,似乎如果我将FasterCSV::parse与块一起使用,它会一次读取一行文件,而不会分配太多内存。如果文件有任何类型的错误,它会抛出FasterCSV::MalformedCSV异常。
我已经实现了一个自定义解决方案,但我不确定它是否是最好的解决方案(请参阅下面的答案)。我有兴趣了解替代方案
我在 Ruby on Rails 应用程序上使用 FasterCSV,目前如果文件无效,它会引发异常。
我查看了FasterCSV 文档,似乎如果我将FasterCSV::parse与块一起使用,它会一次读取一行文件,而不会分配太多内存。如果文件有任何类型的错误,它会抛出FasterCSV::MalformedCSV异常。
我已经实现了一个自定义解决方案,但我不确定它是否是最好的解决方案(请参阅下面的答案)。我有兴趣了解替代方案
这是我目前的解决方案。我真的很想知道改进/替代方案。
# /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
我昨天做了一些测试,结果发现我的解决方案不太奏效;在实施第一个is_valid
. 我不确定这是 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
接受块并逐行解析文件的a,但就我的目的而言,这个简单的实现就足够了,它适用于所有情况。
我假设您想解析 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 通信并隐藏解析结果的类中,因为我认为我的示例不是线程安全的 :)