challenge RFC 4180対応版 CSVレコードの分解

ある関数(splitCSV)に渡された文字列を配列に分解して列ごとに表示してください。
渡される文字列は、CSVデータの1レコードが設定されているとします。

使用するデータはK3形式が元になっている仕様で
エクセルが出力しているような形式です。

書式には次のような特徴があります。
1. 各レコードは「改行」によって区切られている。
2. 各列は「,」によって区切られている。
3. 列のデータは「"」によって囲んでも良い。
4. 列に「,」「改行」「"」いずれかを含む場合「"」で
   囲わなければならない。
5. 列データに「"」を含める場合「""」とする。

本来、改行コードはCRLFですが今回は特に指定しません。

次の入力があった場合
"aaa","b
bb","ccc",zzz,"y""Y""y",xxx

出力は
1 => aaa
2 => b
bb
3 => ccc
4 => zzz
5 => y"Y"y
6 => xxx

となります。
このお題はraynstardさんの投稿によるものです。ご投稿ありがとうございます。助かります。

Posted feedbacks - Groovy

正規表現等で頑張ってみました。
すっきり書けたかなと思います。

[実行結果]
1 => aaa
2 => b
bb
3 => ccc
4 => zzz
5 => y"Y"y
6 => xxx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def csv = '''\
"aaa","b
bb","ccc",zzz,"y""Y""y",xxx\
'''
resolveCSV(csv).eachWithIndex{ it, idx -> println "${idx+1} => ${it}" }

/** CSVレコードの分解(RFC 4180対応版) */
def resolveCSV(String csv) {
    csv.split(',').inject(['""']){ result, it ->
        // 一個前が「半開」ならそこに追加
        if (result[-1] ==~ /^".*[^"]$/ || result[-1].count('"') % 2 == 1) {
            result[-1] = [result[-1], it].join(',') // 「,」で結合
        // 一個前が「閉」なら新しく要素を追加
        } else {
            // 「"」で囲んでおく
            result << ((it =~ /"/) ? it : '""'.toList().join(it))
        }
        result
    }[1..-1]*.replaceAll(/^"|"$/, '')*.replaceAll(/"{2}/, '"')
}

opencsvライブラリを使用しました。 (opencsv - an open source csv parser for Java <http://opencsv.sourceforge.net/>)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import au.com.bytecode.opencsv.*
import java.io.*

def csv = '''\
"aaa","b
bb","ccc",zzz,"y""Y""y",xxx\
'''

new CSVReader(new StringReader(csv)).readAll().each{
    def i = 1
    it.each{
        println "${i++} => ${it}"
    }
}

Index

Feed

Other

Link

Pathtraq

loading...