rksoftware

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

C# で CSV を読み書き (ライブラリ使わず)

以前に C# でライブラリを使って CSV を読み込む方法を試しました。
rksoftware.hatenablog.com

しかし少々コードが長くなってしまったので、手軽に使えるスニペットを書いてみました。CSV の読み書き、何度かいても忘れてしまうのでもう書き残しておくことにします。
こんな感じのコードをプロジェクト内においておけば、しゅっと CSV が扱えるかもしれません。品質は保証できませんけれど。

■ 使い方

var csvString = "a,b,c\n1,2,3\n4,5,6\n";
var data = CsvParser.CsvReader.Read(csvString);
var csvString = "a,b,c\n1,2,3\n4,5,6\n";
var (header, data) = CsvParser.CsvReader.ReadHeaderAndData(csvString);

■ CSV 読み込みコード

public static class CsvReader
{
    public static string[][] Read(string csvString) => _Read(csvString);

    public static (string[] header, string[][] data) ReadHeaderAndData(string csvString)
    {
        var values = Read(csvString);
        return (values.FirstOrDefault() ?? Array.Empty<string>(), values.Skip(1).ToArray());
    }

    private static string[][] _Read(string csvString)
    {
        List<List<List<char>>> data = new();
        (bool isLineHead, bool isQuoted, char quote, bool isEscaped) = (true, false, '"', false);
        foreach (var c in csvString)
        {
            if (isLineHead) { data.Add(new List<List<char>>()); data.LastOrDefault()?.Add(new List<char>()); isLineHead = false; }
            if (data.LastOrDefault()?.LastOrDefault()?.Count == 0 && !isQuoted && (c == '"' || c == '\''))
            {
                (isQuoted, quote, isEscaped) = (true, c, false);
                continue;
            }
            if (!isEscaped && isQuoted && c == quote) { isEscaped = true; continue; }
            if (c == ',' && (!isQuoted || isEscaped)) { data.LastOrDefault()?.Add(new List<char>()); (isQuoted, isEscaped) = (false, false); continue; }
            if (c == '\n' && (!isQuoted || isEscaped)) { isLineHead = true; (isQuoted, isEscaped) = (false, false); continue; }
            isEscaped = false;
            data.LastOrDefault()?.LastOrDefault()?.Add(c);
        }
        return data.Select(line => line.Select(cell => new string(cell.ToArray())).ToArray()).ToArray();
    }
}

■ CSV 文字列化コード

public static class CsvWriter
{
    public static string Write(string[][] data) => _Write(data);

    public static string Write(string[] header, string[][] data) => Write(new string[][] { header }.Concat(data).ToArray());

    private static string _Write(string[][] data)
    {
        var sb = new System.Text.StringBuilder();
        foreach (var line in data ?? Array.Empty<string[]>())
            sb.Append(string.Join(',', line.Select(Escape)) + '\n');
        return sb.ToString();

        static string Escape(string cell) =>
            (cell.Contains('"') || cell.Contains('\'') || cell.Contains(',') || cell.Contains('\n'))
            ? $"\"{cell.Replace("\"", "\"\"")}\""
            : cell;
    }
}

■ GitHub

このコードは GitHub においています。

github.com