4

受信データファイルには、エスケープされていない引用符などの不正な形式のCSVデータと、新しい行を含むフィールドなどの(有効な)CSVデータが含まれています。CSV形式のエラーが検出された場合は、そのデータに対して別のルーチンを使用したいと思います。

次のサンプルコード(簡単にするために省略)

FasterCSV.open( file ){|csv|
  row = true
  while row
    begin
      row = csv.shift
      break unless row
      # Do things with the good rows here...

    rescue FasterCSV::MalformedCSVError => e
      # Do things with the bad rows here...
      next
    end
  end
}

MalformedCSVErrorは、csv.shiftメソッドで発生します。レスキュー句からエラーの原因となったデータにアクセスするにはどうすればよいですか?

4

3 に答える 3

8
require 'csv' #CSV in ruby 1.9.2 is identical to FasterCSV

# File.open('test.txt','r').each do |line|
DATA.each do |line|
  begin
    CSV.parse(line) do |row|
      p row #handle row
    end
  rescue  CSV::MalformedCSVError => er
    puts er.message
    puts "This one: #{line}"
    # and continue
  end
end

# Output:

# Unclosed quoted field on line 1.
# This one: 1,"aaa
# Illegal quoting on line 1.
# This one: aaa",valid
# Unclosed quoted field on line 1.
# This one: 2,"bbb
# ["bbb", "invalid"]
# ["3", "ccc", "valid"]   

__END__
1,"aaa
aaa",valid
2,"bbb
bbb,invalid
3,ccc,valid

ファイルを1行ずつFasterCSVにフィードして、エラーを救済するだけです。

于 2011-10-06T08:04:18.287 に答える
2

これは本当に難しいでしょう。FasterCSVを高速化するいくつかの要因により、これは特に困難になります。これが私の最善の提案です:FasterCSVはIOオブジェクトをラップできます。次に、最後の結果を「保持」する独自のサブクラス(それ自体がのサブクラス)を作成することができます。次に、FasterCSVが例外を発生させたときに、特別なオブジェクトに最後の行を要求できます。このようなもの:FileIOgetsFile

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

ちょっときちんとしていますよね?しかし!もちろん、注意点があります。

  1. getsすべての呼び出しにステップを追加すると、パフォーマンスがどの程度低下するかわかりません。数百万行のファイルをタイムリーに解析する必要がある場合は、問題になる可能性があります。

  2. これ完全に失敗するCSVファイルの引用符で囲まれたフィールド内に改行文字が含まれている場合、失敗する場合と失敗しない場合があります。この理由はソースに記載されています。基本的に、引用符で囲まれた値に改行が含まれている場合は、行全体を取得するためにshift追加getsの呼び出しを行う必要があります。この制限を回避する賢い方法があるかもしれませんが、それは今私には来ていません。ただし、引用符で囲まれたフィールド内にファイルの改行文字が含まれていないことが確実な場合は、これは心配する必要はありません。

他のオプションは、を使用してファイルを読み取り、各行をFile.gets順番に渡すFasterCSV#parse_lineことですが、そうすることで、FasterCSVを使用することで得られるパフォーマンス上の利点を無駄にすることになると確信しています。

于 2011-10-06T08:05:41.753 に答える
1

CSVが入力データを解析しようとする前に、Jordanのファイルサブクラス化アプローチを使用して入力データの問題を修正しました。私の場合、CSVで期待される「」ではなく、「」を使用して引用符をエスケープするファイルがありました。したがって、

class MyFile < File
  def gets(*args)
    line = super
    if line != nil
      line.gsub!('\\"','""')  # fix the \" that would otherwise cause a parse error
    end
    line
  end
end

infile = MyFile.open(filename)
incsv = CSV.new(infile)

while row = infile.shift
  # process each row here
end

これにより、非標準のCSVファイルを解析できました。RubyのCSV実装は非常に厳密であり、CSV形式の多くのバリエーションで問題が発生することがよくあります。

于 2012-10-02T23:54:22.003 に答える