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 - Other

PEG (Parsing expression grammar) ベースのパターンマッチ OOPL である OMeta で。

OMeta には、COLA(Combined Object-Lambda Architecture; aka, Pepsi&Coke)、Squeak Smalltalk、JavaScript での実装がありますが、ここでは Squeak OMeta を用い、Doukaku33 として定義しました。

実行例
| in record |
in := '"aaa","b
bb","ccc",zzz,"y""Y""y",xxx'.
record := (Doukaku33 onTree: nil) apply: #レコード withArguments: in.
World findATranscript: nil.
record doWithIndex: [:field :idx | Transcript cr; show: idx; show: ' => ', field]

出力
1 => aaa
2 => b
bb
3 => ccc
4 => zzz
5 => y"Y"y
6 => xxx
1
2
3
4
5
レコード     ::= <列>:first ($, <列>)*:rest => [rest addFirst: first; yourself]
列           ::= (<クオートあり> | <クオートなし>):xs => [String withAll: xs]
クオートあり ::= $" ($" $" => [$"] | ~$" <char>)+:xs $" => [xs]
クオートなし ::= (~(<改行> | $, | $") <char>)*
改行         ::= <exactly (Character cr)>

VBScriptはテキスト加工に適した言語なのですが、
まだ投稿がなかったのでやってみました。
C#版の単純移植なので、あまり綺麗なコードではありません。。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
s = """aaa"",""b" & vbCrLf & "bb"",""ccc"",zzz,""y""""Y""""y"",xxx"
ss = splitCSV(s)
i = 1
For Each elem In ss
    WScript.Echo i & " => " & elem
    i = i + 1
Next

Function splitCSV(s)
    If IsNull(s) Then
        splitCSV = Null
        Exit Function
    End If


    Dim a()
    index = 0
    result = ""

    i = 1
    While i <= Len(s)
        b = (mid(s, i, 1) = """")

        If b Then
            i = i + 1
        End If

        If b Then
            j = InStr(i, s, """")
        Else
            j = InStr(i, s, ",")
        End If

        If j < 1 Then
            j = Len(s) + 1
        End If

        t = mid(s, i, j - i)

        If b And (j < Len(s) - 1) And Mid(s, j+1, 1) = """" Then
            result = result & t
            result = result & """"
            i = j + 1
        Else
            ReDim Preserve a(index)
            a(index) = result & t
            index = index + 1
            result = ""
            If b Then
                i = j + 2
            Else
                i = j + 1
            End If
        End If
    Wend

    splitCSV = a
End Function

Index

Feed

Other

Link

Pathtraq

loading...