2018年5月11日金曜日

VB CSV でコンマ混在データをハンドリング

msdn を見ていて、これは、自分でも書いて置こうと思ったご質問があります。
書くのは、投稿ではなく、コードです。  :) 。
CSV の中に コンマ が混ざったら?。
此れ迄、余り、経験の無かった 世界ですね。


何事も経験!。
幾ら、コードが Internet 上に転がっていても、
見ているだけでは自分のものにはなりません。
書いて見る! それが 自分のものにする 最短の道 です。

今回のそれは CSV データの中にコンマが混ざっていたらどうすれば良いか?、 です。
私は、CSV をハンドリングする機会はそうはありません。
ですから、! だったのです。

CSV は、ご存知の通り、コンマでデータのブロックを区別します。
その名前の通りですね。

データの中にコンマが入り込んでは区別が付きませんから。

数字の区切り記号としてのコンマなら、
それを外して、数値として扱う方法も有りますし、
表示にはそれなりの文字列置換や書式設定を行えば良い訳です。

でも、文字列の中に、コンマが登場するとしたら、
下手に置換すれば、より複雑怪奇な事態を招き兼ねない場合もあるでしょう。

これは、自分でも書いて、ものにして置こう! と思った訳です。

思い出して見ましょう。
例えば、ファイル名やパス名に 半角スペース が混ざった時の Windows での お決り !。
そうです。
" や ' で囲ってあげる! でしたね。
こうする事で、半角スペースが意味する パラメータの区切りと データ内のそれとを区別させるのでしたね。

CSV のコンマにも同じ手法が使えます。

じゃぁ、プログラムでどう書くのか?。
はい、 ちゃんと、方法が用意されているのですね。

キーワードは TextFieldParser です。

CSV ファイルに、この問題が起こるケースが多いからか、
ファイルから読み取る場合を想定して、クラスが用意してあるのですね。
そのクラスは Microsoft.VisualBasic.FileIO.TextFieldParser です。

以下に、コンマの扱いがどう変わるか、
代表的な Split と比較したプログラムを書いて見ました。
コンマがあったら、改行するだけの簡単なテストプログラムです。

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Dim csv(1) As String
        'csv(0) = "10,100,1,000,10,000"
        'csv(1) = """" & "10" & """" & "," & _
        '         """" & "100" & """" & "," & _
        '         """" & "1,000" & """" & "," & _
        '         """" & "10,000" & """"
        'Me.Label1.Text = "CSV : Lines" & vbCrLf & csv(0) & vbCrLf & csv(1)
        ' > To File tes.csv / Project Content / Action : Copy
        '                        10,100,1,000,10,000
        '                        "10","100","1,000","10,000"

        Dim csvFile As String = "test.csv"
        Me.Label1.Text = "CSV : Lines" & vbCrLf & ReadCSV(csvFile)

        'split
        Me.Label2.Text = "Read by Split" & vbCrLf & ReadCSVbySplit(csvFile)
        'TextFieldParser
        Me.Label3.Text = "Read by Parser" & vbCrLf & ReadCSVbyParser(csvFile)

    End Sub

    Private Function ReadCSV(ByVal filePath As String) As String
        Dim value1 As String = ""
        Using swText As New System.IO.StreamReader(filePath, _
                                                   System.Text.Encoding.Default)
            value1 = swText.ReadToEnd
        End Using

        Return value1
    End Function

    Private Function ReadCSVbySplit(ByVal filePath As String) As String
        Dim value2 As String = ""
        Dim sb2 As New System.Text.StringBuilder
        Using sw As New System.IO.StreamReader(filePath, _
                                               System.Text.Encoding.Default)
            While sw.Peek > -1
                Dim swLine As String = sw.ReadLine()
                If Not swLine Is Nothing Then
                    Dim lineElm As String() = swLine.Split(",")
                    For Each item In lineElm
                        sb2.Append(item)
                        sb2.Append(vbCrLf)
                    Next
                End If
                sb2.Append("------")
                sb2.Append(vbCrLf)
            End While

            value2 = sb2.ToString
        End Using

        Return value2
    End Function

    Private Function ReadCSVbyParser(ByVal filePath As String) As String
        Dim value3 As String = ""
        Dim sb3 As New System.Text.StringBuilder

        Using parser As New Microsoft.VisualBasic.FileIO.TextFieldParser(filePath, _
                                                                         System.Text.Encoding.Default)

            parser.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
            parser.SetDelimiters(",")

            ' parser.HasFieldsEnclosedInQuotes = False
            ' parser.TrimWhiteSpace = False

            While Not parser.EndOfData
                Dim row As String() = parser.ReadFields()

                For Each field In row
                    sb3.Append(field)
                    sb3.Append(vbCrLf)
                Next
                sb3.Append("------")
                sb3.Append(vbCrLf)
            End While
        End Using

        Return sb3.ToString
    End Function

End Class


VB.net で Form に Label を3つ配置し、
プロジェクトには test.csv と言うファイルを用意し、
ビルドアクションは コンテンツ、出力ディレクトリにコピー とします。

すると、こんな画面が登場します。



便利なものですねぇ。


勿論、このクラスを使わなく共、
必ず CSV にする折に前後を ” 等で括る と決めているなら、
分割した文字列が " で始まっていなければ、 , を付加して前の項目に繋げて上げれば良いのですが、
それより、このクラスを使った方が簡単で、後からも見易いですね。
両端 " の有無にも影響されにくいですし。   (混在可).



0 件のコメント:

コメントを投稿