rksoftware

Visual Studio とか C# とかが好きです

C# で CSV を読み込む

結構面倒です。

すばらしいライブラリがあるので、ライブラリを使えばいいのですがちょっと重たい気がしたのでメモ。

■ ここでいう重たいとは

インスタンスを作って、動作の設定をセットして、while でわましながら読んだり。セルの読み出しが手間だったりをいうことをイメージしています。 static メソッドに string を渡して、string の二次元配列が返ってくるくらいのシンプルさが欲しい時もあります。というか結構そういうときのほうが多いかもしれないです。

■ どうして

ライブラリはお行儀がいいので、メモリに文字列をため込まない = string でなく stream で受け取る、一気に読まず while で回す、配列で返さずデータクラスのプロパティにマッピングする。いろいろとそういう感じだと思います。

しかし我々はお行儀が悪いので。

ライブラリがお行儀よくでも使う我々はお行儀が悪いので、配列でもらって加工したい、そもそも読むファイルが全行のセルが同じ数だと思うなよ! そんな感じです。

■ ライブラリを見てみよう!

今回見るライブラリは 3 つ。

  • Csv で検索すると出てきてダウンロード数が結構ある Csv
  • CsvHelper 皆大好きなライブラリです。しかし今は以前からは変更が入って使い方がかなり変わっています。データクラス定義しないで使う方法に結構苦しみました。
  • VisualBasic ライブラリの TextFieldParser。古来より受け継がれし遺跡です。.NET Framework 以前の Visula Basic 時代の便利機能が入っているようです。使ったら負けかなと思う由来ですが、正直便利です。TextFieldParser も、正直便利です。
//"a
// b",c""d
// e, f
using Csv;
using CsvHelper.Configuration;

var csv = """
"a
b","c""d"
e,f
""";
Console.WriteLine($"csv ->\n{csv}\n<- csv\n");

{   // Csv というライブラリ
    // dotnet add package Csv
    IEnumerable<Csv.ICsvLine> csvLines = Csv.CsvReader.ReadFromText(csv, new CsvOptions { HeaderMode = HeaderMode.HeaderAbsent });
    Console.WriteLine("Csv ->");
    foreach (Csv.ICsvLine line in csvLines)
        Console.WriteLine($"  row -> {string.Join(" , ", line.Values)}  <- row");
    Console.WriteLine("<- Csv\n");
}

{   // CsvHelper
    // dotnet add package CsvHelper
    using StringReader sr = new StringReader(csv);
    using var helper = new CsvHelper.CsvReader(sr, new CsvConfiguration(System.Globalization.CultureInfo.CurrentCulture) { HasHeaderRecord = false });
    Console.WriteLine("CsvHelper ->");
    while (helper.Read())
    {
        Console.Write($"  row -> ");
        Console.Write(string.Join(" , ", Enumerable.Range(0, helper.Parser.Count).Select(i => helper.Parser[i])));
        Console.WriteLine("  <- row");
    }
    Console.WriteLine("<- CsvHelper\n");
}

{   // VisualBasic (TextFieldParser)
    // dotnet add package Microsoft.VisualBasic
    using StringReader sr = new StringReader(csv);
    using var tfp = new Microsoft.VisualBasic.FileIO.TextFieldParser(sr);
    tfp.SetDelimiters(",");
    Console.WriteLine("VisualBasic (TextFieldParser) ->");
    while (!tfp.EndOfData)
        Console.WriteLine($"  row -> {string.Join(" , ", tfp.ReadFields() ?? Array.Empty<string>())}  <- row");
    Console.WriteLine("<- VisualBasic (TextFieldParser)");
}

■ 実行結果

csv ->
"a
b","c""d"
e,f
<- csv

Csv ->
  row -> "a  <- row
  row -> b" , c"d  <- row
  row -> e , f  <- row
<- Csv

CsvHelper ->
  row -> a
b , c"d  <- row
  row -> e , f  <- row
<- CsvHelper

VisualBasic (TextFieldParser) ->
  row -> a
b , c"d  <- row
  row -> e , f  <- row
<- VisualBasic (TextFieldParser)
  • Csv はセルの中に改行が許されません。
  • CsvHelper はデータはちゃんと読めていますが、配列でほしい時にちょっとインタフェースから取り方がわかりにくいですね。
  • TextFieldParser はもう少し配列でとりやすいですが、お行儀悪く使うにはやはり重いですね。

ままならないものです。お行委¥魏悪く生きるというのは。

いかがでしたか?

いかがでしたか?