rksoftware

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

C# で CSV 文字列を CSV としてパースして指定のセルでソートして出力するコード

意外と難しかったのでメモ。

CsvHelper というライブラリを使っています。

導入方法

dotnet add package CsvHelper

C# で CSV 文字列を CSV としてパースして指定のセルでソートして出力するコードです。内部的にはデータを配列で扱っています。CSV の中身には決まりがないのでデータオブジェクトにマッピングしない、というスタンスでやってみました。

■ コード

// データの読み込みと加工
var (header, data) = ReadAndOrder(args.FirstOrDefault() ?? string.Empty, args.Skip(1).FirstOrDefault() ?? string.Empty);

// 結果の出力 ヘッダー行とデータ行
Console.WriteLine(string.Join(",", header.Select(Escape)));
Console.WriteLine(string.Join("\n", data.Select(line => string.Join(",", line.Select(Escape)))));


////
// == 以降メソッド ==
////

// データの読み込みと加工メソッド
static (string[], string[][]) ReadAndOrder(string csv, string orderCellName)
{
    // データの読み込みとヘッダー行の抽出
    var headerAndData = Read(csv).ToArray();
    var header = headerAndData.FirstOrDefault() ?? Array.Empty<string>();

    // ソートする列のインデックスを産出
    var orderCellIndex = header.Index().Where(x => string.Compare(x.Item, orderCellName) == 0).Select(x => x.Index + 1).FirstOrDefault() - 1;

    // ソートしつつデータ部の抽出
    var data = headerAndData.Skip(1).OrderBy(x => (-1 < orderCellIndex && orderCellIndex < x.Length) ? x[orderCellIndex] : "").ToArray();

    return (header, data);
}

// 読み込みメソッド CsvHelper 使用
static IEnumerable<string[]> Read(string csv)
{
    using var sr = new StringReader(csv);
    using var helper = new CsvHelper.CsvReader(sr, System.Globalization.CultureInfo.CurrentCulture);
    while (helper.Read())
        yield return Enumerable.Range(0, helper.Parser.Count).Select(i => helper.Parser[i]).ToArray();
}

// 結果出力時のエスケープ
static string Escape(string cell) =>
    (cell.Contains('"') || cell.Contains('\'') || cell.Contains(',') || cell.Contains('\n'))
    ? $"\"{cell.Replace("\"", "\"\"")}\""
    : cell;

■ 実行例

h1 列でソート

.\kaito.exe h1,h2`n3,6`n5,4`n6,5`n4,3 h1
h1,h2
3,6
4,3
5,4
6,5

h2 列でソート

.\kaito.exe h1,h2`n3,6`n5,4`n6,5`n4,3 h2
h1,h2
4,3
5,4
6,5
3,6

■ C# Tokyo お題

このコードは C# Tokyo のお題のコードを書いてみよう企画で書いたコードです。

github.com