けいじゅ@日本ラショナルソフトウェアです. In [ruby-list :18477 ] the message: "[ruby-list:18477] [book] csv_split ", on Nov/08 23:11(JST) Kazuhiro Nishiyama writes: >こんばんは、ZnZです。 どもども. >csv_split1は、列が「"」で終わる場合に >例外(cannot decode CSV (RuntimeError))が発生します。 うーん.... こんな感じでしょうか... 正規表現がだんだん複雑になるのが気が重いけど... # (アルゴリズムの本質を変えない)エレガントな解答求む. -- ### csv_split def csv_split(source, delimiter = ',') csv = [] # splitした結果が入る. data = "" # 解析しているフィールドの文字列が入る. #(1) 文字列をデリミタで区切り, その区切られた文字列毎にeachで処理する. source.split(delimiter).each do |d| # (2) 継続フィールドの処理(フィールドが`"'で始まっている時のため) if data.empty? data = d else data += delimiter + d end if /^"/ =~ data # (3.1) 文字列が`"'で始まっている時の処理 if /(^"|[^"])("")*"$/ =~ data # (3.1.1) `"'で終了している時の処理 # (A) "で始まるフィールドの処理 csv << data.sub(/^"(.*)"$/, '\1').gsub(/""/, '"') data = '' end else # (3.2) `"'で始まっていない時の処理 csv << d data = '' end end raise "cannot decode CSV\n" unless data.empty? csv end -- >csv_split3は行が「"」で終わる場合に例外が発生します。 >./csv_split3.rb:41:in `concat': failed to convert nil into String (TypeError) > from ./csv_split3.rb:41:in `csv_split_for_quoted_field' > from ./csv_split3.rb:7:in `csv_split' > >また、のcsv_split3は最後の列が空の時に無視されるようです。 > > >p csv_split(',""""') # =>どちらも例外 >p csv_split(',"""",') # =>csv_split3だと["", "\""]になる。 うーん. うーん. 前者の問題は解決が楽だけど, 後者は.... csv_split2と同じく番兵(?)を置くこ とにしました. # こちらもエレガントな解答求む -- ### csv_split3 # (1) csv_splitの本体 def csv_split(source, delimiter = ',') chars = (source+",").split(//) # (A) 文字列を1文字ずつに分解する csv = [] while c = chars.shift # 配列の先頭から1要素取り出し, その # 要素を配列から削除する case c when '"' # 配列csvにフィールドを追加. csv.push csv_split_for_quoted_field(chars, delimiter) else # 先ほど取り出した文字を戻す. chars.unshift c csv.push csv_split_for_normal_field(chars, delimiter) end end csv end # (2) 通常のフィールドの処理 def csv_split_for_normal_field(chars, delimiter) field = "" while c = chars.shift case c when delimiter return field else field.concat c # 文字列fieldの最後に文字列cを追加 end end field end # (3) `"'で始まるフィールドの処理 def csv_split_for_quoted_field(chars, delimiter) field = "" while c = chars.shift case c when '"' c1 = chars.shift if c1 == delimiter or c1.nil? return field elsif c1 == '"' field.concat '"' else # (B-1) 不正フォーマット field.concat c field.concat c1 end else field.concat c end end # (B-2) 不正フォーマット field end __ ..............................石塚 圭樹@日本ラショナルソフトウェア... ----------------------------------->> e-mail: keiju / rational.com <<---