DataTableのカラム定義

タグの編集
投稿者 ディグダ  (社会人) 投稿日時 2022/11/17 14:40:30
CSVからDataTableにセットしようとしているのですが、以下のコードで列がテーブルに属していないとエラーが出ます。
列名を変数で管理しているのがいけないのでしょうか?
よろしくお願いいたします。

        Dim dt As New DataTable
        Dim dtrow As DataRow
        Do Until ReadFile.AtEndOfStream
            sReadCsv = ReadFile.ReadLine
            Select Case colum_setflg
                Case False
                    record_colum = sReadCsv.Split(","c)
                    For cnt As Integer = 0 To record_colum.Count - 1
                        'ダブルクォーテーションを削除
                        dt.Columns.Add(record_colum(cnt).Trim(""""c, """"c))
                    Next
                    colum_setflg = True
                Case True
                    dtrow = dt.NewRow
                    record_row = sReadCsv.Split(","c)
                    For cnt As Integer = 0 To record_colum.Count - 1
                        'ダブルクォーテーションを削除
                        dtrow(record_colum(cnt)) = record_row(cnt).Trim(""""c, """"c)
                    Next
                    dt.Rows.Add(dtrow)
            End Select
        Loop
投稿者 SSD  (社会人) 投稿日時 2022/11/17 15:18:11
列を追加したときは
record_colum(cnt).Trim(""""c, """"c)
となっていますが、列を指定するときは
record_colum(cnt)
となっているからではないでしょうか?
(=列の指定時にダブルクォーテーションが削除されていない)
投稿者 ディグダ  (社会人) 投稿日時 2022/11/17 15:34:11
なるほど、確かにそうでした。
ありがとうございます。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/11/17 15:34:45
列のスペルが colum 表記になっているのが、なんだか舌足らずでソワソワします…。

それはさておき、個々の変数宣言などが省略されていますし、CSV ファイルの内容も分かりません。
状況を推察しやすいよう、第三者が再現しやすいコードの提示をお願いします。
断片的な情報から、ある程度類推はできますが、それらの情報は質問者の方から提示されるべきかと。


> 以下のコードで列がテーブルに属していないとエラーが出ます。
どの場所で何というエラーメッセージが生じていますか?
また、エラー発生時点で、各変数の中身が何を指しているのか確認されましたか?

たとえば
 dtrow(record_colum(cnt)) = record_row(cnt).Trim(""""c, """"c)
の時点でエラーになるのだとしたら、
 •エラー発生時の cnt の値は幾つか?
 •record_colum(cnt) の中身は何になっているか? Nothing や "" では無いか?
などをチェックしてみてください。

なお現状のコードだと、ループ開始前に colum_setflg の初期値が
False では無かった場合、列数ゼロの DataTable への行追加になってしまいますし、
record_colum の配列の中身も書き換わらないはずです。


細かい話をすれば、行によって「,」の数が異なっている可変列数 CSV の場合や、
「同じ列名が重複定義されていた場合」、「先頭行が列名ではない CSV」の扱い、
データ中に「,」や改行を含む CSV などをどうするか…という疑問もあります。
これらは前提条件で弾くことができそうなので、プログラムそのものの問題とは別件ですけれどね。



> ReadFile.AtEndOfStream
これってもしかして、COM の Scripting.TextStream オブジェクトでしょうか。
VB.NET なら、System.IO 名前空間のファイルを使った方が良いですよ。

代替データストリームなどのように、System.IO だとアクセスできない情報を
扱うといった特殊な事情が無い限りは。


> .Trim(""""c, """"c)
同じ char を 2 つ連記しているのは何故ですか?


コードを見る限り、先頭行の列名を使ってアクセスしているように見えますが、
dtrow(x) の x は Integer でも String でも構わないのですから、わざわざ
record_colum という String 配列を用意する必要は無さそうです。


Dim dt As New DataTable()
Dim isFirstRow As Boolean = True
Do Until ReadFile.AtEndOfStream
    Dim line As String = ReadFile.ReadLine()

    Dim cols As String() = line.Split(","c).Select(Function(cell) cell.Trim(""""c)).ToArray()
    If isFirstRow Then
        isFirstRow = False
        For Each colName As String In cols
            dt.Columns.Add(colName)
        Next
    Else
        dt.Rows.Add(cols)
    End If
Loop
dt.AcceptChanges()
投稿者 SSD  (社会人) 投稿日時 2022/11/17 16:07:39
蛇足なんですが、Microsoft.VisualBasic.FileIO.TextFieldParserクラスを使うと便利だと思います。
区切り文字や、ダブルクォーテーションがあるかないかを一度設定すれば都度分解したり削除したりする手間が省けます。
以下で紹介されています。
http://www.mitene.or.jp/~rnk/vbdotnet/csvread.html
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/11/17 16:30:31
> 蛇足なんですが、Microsoft.VisualBasic.FileIO.TextFieldParserクラスを使うと便利だと思います。
TextFieldParser は、「空行を握りつぶしてしまう」という制限があるので、
その点は注意が必要ですね。

たとえば、 "一行目{改行}{改行}{改行}四行目" のようなデータがあったら、
空行が握りつぶされて "一行目{改行}四行目" というデータに変化してしまったかと。


> 以下で紹介されています。
蛇足ついでに、nuget で取得できるその他の有名なライブラリを紹介しておきます。
下記は VB の使用例では無く、C# の使用例ですが…。
https://tech-and-investment.com/csv1/


とはいえ、データ内に改行やカンマが含まれないようなものな単純なものであれば、
自分も今回の元ソースのように、自分で Split するだけで済ませることも多いです。