Это будет действительно сложно. Некоторые вещи, которые делают FasterCSV, ну, быстрее , делают это особенно трудно. Вот мое лучшее предложение: FasterCSV может обернуть объект IO . То, что вы могли бы сделать, это сделать свой собственный подкласс File
(сам подкласс IO
), который "держит" результат последнего gets
. Затем, когда FasterCSV вызывает исключение, вы можете запросить у вашего специального объекта File
последнюю строку. Примерно так:
class MyFile < File
attr_accessor :last_gets
@last_gets = ''
def gets(*args)
line = super
@last_gets << $/ << line
line
end
end
# then...
file = MyFile.open(filename, 'r')
csv = FasterCSV.new file
row = true
while row
begin
break unless row = csv.shift
# do things with the good row here...
rescue FasterCSV::MalformedCSVError => e
bad_row = file.last_gets
# do something with bad_row here...
next
ensure
file.last_gets = '' # nuke the @last_gets "buffer"
end
end
Вроде аккуратно, верно? НО! Есть, конечно, предостережения:
Я не уверен, какую долю производительности вы получаете, когда добавляете дополнительный шаг к каждому вызову gets
. Это может быть проблемой, если вам нужно своевременно анализировать файлы с несколькими миллионами строк.
Этот завершается с ошибкой может или не может произойти сбой, если ваш CSV-файл содержит символы новой строки внутри полей в кавычках. Причина этого описана в источнике - в основном, если значение в кавычках содержит символ новой строки, тогда shift
должен сделать дополнительные вызовы gets
, чтобы получить всю строку. Может быть разумный способ обойти это ограничение, но оно не придет ко мне прямо сейчас. Если вы уверены, что в вашем файле нет символов новой строки в полях в кавычках, это не должно вас беспокоить.
Ваш другой вариант будет читать файл с помощью File.gets
и передавать каждую строку по очереди FasterCSV#parse_line
, но я уверен, что при этом вы растрачивать любое преимущество в производительности, полученное при использовании FasterCSV.