2012年11月20日火曜日

正規表現を使わないでCSVをパース

CSVファイルを読み込んでDataGridに表示」では、正規表現を使ってCSVをパースしましたが、ここでは別の方法を使っています。
入力文字列を一文字ずつ読み進めていき、

  • 「ダブルクォート」がきたら、そのダブルクォートが"閉じる"までの文字列を項目とする
  • 「カンマ」がきたら項目を追加
  • 「改行」がきたら行を追加
しています。
1行は、Queue<string> に格納し、さらに行の集合を Queue に格納しています。
CSVファイルを読み込んでDataGridに表示」の DataTable GetCsvDataTable(string content) メソッドを以下のように書き換えます。



private DataTable GetCsvDataTable(string content)
{
    string text = content.TrimEnd('\r', '\n');

    Queue<Queue<string>> lines = new Queue<Queue<string>>();
    Queue<string> line = new Queue<string>();
    lines.Enqueue(line);
    StringBuilder currentToken = new StringBuilder();
    bool isInDoublequote = false;

    for (int i = 0; i < text.Length; i++)
    {
        char c = text[i];
        if (isInDoublequote)
        {
            if (c == '"')
            {
                if ((i + 1 < text.Length && text[i + 1] != '"') || i == text.Length - 1)
                {
                    isInDoublequote = false;
                }
                else if (i + 1 < text.Length && text[i + 1] == '"')
                {
                    currentToken.Append('"');
                    i++;
                }
            }
            else
            {
                currentToken.Append(c);
            }
        }
        else
        {
            if (c == ',')
            {
                line.Enqueue(currentToken.ToString());
                currentToken.Clear();
            }
            else if (c == '"')
            {
                isInDoublequote = true;
            }
            else if (c == '\r' || c == '\n')
            {
                line.Enqueue(currentToken.ToString());
                if (i + 1 < text.Length && c == '\r' && text[i + 1] == '\n')
                {
                    i++;
                }
                line = new Queue<string>();
                lines.Enqueue(line);
                currentToken.Clear();
            }
            else
            {
                currentToken.Append(c);
            }
        }
    }
    line.Enqueue(currentToken.ToString());

    // データテーブルにコピー
    DataTable dt = new DataTable();
    int columnLength = lines.Peek().Count;
    for (int i = 0; i < columnLength; i++)
    {
        dt.Columns.Add();
    }
    while (lines.Count != 0)
    {
        Queue<string> row = lines.Dequeue();
        DataRow dr = dt.NewRow();
        int count = 0;
        while (row.Count != 0)
        {
            dr[count] = row.Dequeue();
            count++;
        }
        dt.Rows.Add(dr);
    }
    return dt;
}


0 件のコメント:

コメントを投稿