4

C# を使用して CSV を解析しようとしています。","ヘッダー数が一致数と等しい場合、正規表現を使用して文字列を検索して読み取りました。

次のような値がある場合、これは機能しません。

"a",""b","x","y"","c"

私の出力は次のとおりです。

'a'
'"b'
'x'
'y"'
'c'

しかし、私が欲しいのは:

'a'
'"b","x","y"'
'c'

これに使用できる正規表現やその他のロジックはありますか?

4

13 に答える 13

12

CSV では、複数行、引用符、さまざまな区切り記号* などを扱う場合、思ったよりも複雑になる可能性があります。私はこれを使用していますが、非常にうまく機能します。

*=一部のロケールでは [tab] を CSV の C として使用していることに注意してください...

于 2008-11-25T08:34:19.407 に答える
10

CSV はコードの再利用の優れた例です。どの csv パーサーを選択しても、独自のパーサーを選択しないでください。 独自の CSV パーサーのローリングをやめる

于 2009-01-06T20:35:09.993 に答える
3

私があなたなら、 FileHelpersを使用します。正規表現は問題ありませんが、読みにくいです。特に、しばらくしてから戻って簡単に修正する場合はなおさらです。

私の心を鍛えるために、迅速で汚い作業C#手順:

public static List<string> SplitCSV(string line)
{
    if (string.IsNullOrEmpty(line))
        throw new ArgumentException();

    List<string> result = new List<string>();

    bool inQuote = false;
    StringBuilder val = new StringBuilder();

    // parse line
    foreach (var t in line.Split(','))
    {
        int count = t.Count(c => c == '"');

        if (count > 2 && !inQuote)
        {
            inQuote = true;
            val.Append(t);
            val.Append(',');
            continue;
        }

        if (count > 2 && inQuote)
        {
            inQuote = false;
            val.Append(t);
            result.Add(val.ToString());
            continue;
        }

        if (count == 2 && !inQuote)
        {
            result.Add(t);
            continue;
        }

        if (count == 2 && inQuote)
        {
            val.Append(t);
            val.Append(',');
            continue;
        }
    }

    // remove quotation
    for (int i = 0; i < result.Count; i++)
    {
        string t = result[i];
        result[i] = t.Substring(1, t.Length - 2);
    }

    return result;
}
于 2008-11-25T11:11:19.793 に答える
2

よく引用される言葉があります:

問題に直面したときに、「分かった、正規表現を使用する」と考える人もいます。現在、彼らには 2 つの問題があります。(ジェイミー・ザウィンスキー)

CSV ファイルには公式の標準がないため (代わりに、わずかに互換性のないスタイルが多数存在します)、実装するものが受信するファイルに適していることを確認する必要があります。必要なものより手の込んだものを実装しても意味がありません。正規表現は必要ないと確信しています。

用語を抽出する簡単な方法を次に示します。基本的には、行をループしてコンマを探し、現在のインデックスが文字列内にあるかどうかを追跡します。

    public IEnumerable<string> SplitCSV(string line)
    {
        int index = 0;
        int start = 0;
        bool inString = false;

        foreach (char c in line)
        {
            switch (c)
            {
                case '"':
                    inString = !inString;
                    break;

                case ',':
                    if (!inString)
                    {
                        yield return line.Substring(start, index - start);
                        start = index + 1;
                    }
                    break;
            }
            index++;
        }

        if (start < index)
            yield return line.Substring(start, index - start);
    }

標準的な警告 - テストされていないコードです。オフバイワン エラーが発生する可能性があります。

制限事項

  • 値を囲む引用符は自動的には削除されません。これを行うには、最後の近くのステートメントの
    直前にチェックを追加します。yield return

  • 単一引用符は、二重引用符と同じ方法ではサポートされていません
    別の boolean を追加してinSingleQuotedString、既存の boolean の名前を に変更し、inDoubleQuotedString両方を同じ方法で処理することができます。(既存のブール値を二重に機能させることはできません。これは、文字列を開始したのと同じ引用符で終了する必要があるためです。)

  • 空白は自動的に削除されない
    一部のツールでは、CSV ファイルのカンマの周囲に空白を導入して、ファイルを「きれいに」します。そうすると、意図的な空白と書式設定の空白を区別することが難しくなります。

于 2008-11-25T08:53:01.770 に答える
1

CsvHelper (私が管理しているライブラリ) またはFastCsvReaderを試してください。どちらもうまく機能します。CsvHelper は書き込みも行います。他のみんなが言っているように、自分で巻かないでください。:P

于 2010-01-19T16:04:12.347 に答える
1

すべての値が引用符で囲まれていることが保証されている場合は、コンマではなく値を探します。

("".*?""|"[^"]*")

これは、「最初の最長一致が勝つ」という事実を利用しています。最初に二重引用符で囲まれた値を検索し、通常の引用符で囲まれた値の優先度を低くします。

囲んでいる引用符を一致の一部にしたくない場合は、次を使用します。

"(".*?"|[^"]*)"

一致グループ 1 の値を探します。

私が言ったように:これが機能するための前提条件は、各値の周りに引用符または二重引用符が保証された整形式の入力です。空の値も引用符で囲む必要があります! 良い副作用は、区切り文字を気にしないことです。カンマ、タブ、セミコロン、スペースなど、何でも構いません。すべてが機能します。

于 2008-11-25T08:17:44.683 に答える
1

FileHelpersは複数行のフィールドをサポートしています。

次のようなファイルを解析できます。

a,"line 1
line 2
line 3"
b,"line 1
line 2
line 3"

データ型宣言は次のとおりです。

[DelimitedRecord(",")]
public class MyRecord
{ 
 public string field1;
 [FieldQuoted('"', QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
 public string field2;
}

使用法は次のとおりです。

static void Main()
{
 FileHelperEngine engine = new FileHelperEngine(typeof(MyRecord));
 MyRecord[] res = engine.ReadFile("file.csv");       
}
于 2010-08-25T16:08:27.637 に答える
1

解析可能な CSV ファイルを作成するには、値内の二重引用符を適切にエスケープする必要があります。これを行う 2 つの標準的な方法は、二重引用符を 2 つの二重引用符を連続して表すか、バックスラッシュ二重引用符で表すことです。これは、次の 2 つの形式のいずれかです。

""

\"

2 番目の形式では、最初の文字列は次のようになります。

"a","\"b\",\"x\",\"y\"","c"

入力文字列がこのような厳密な形式に対してフォーマットされていない場合、自動化された環境で正常に解析できる可能性はほとんどありません。

于 2008-11-25T08:13:42.567 に答える
0

Lumenworks CSVパーサー(オープンソース、無料ですが、codeprojectログインが必要です)は、私が使用した中で断然最高のものです。正規表現を作成する手間が省け、直感的に使用できます。

于 2008-11-25T09:27:13.840 に答える
0

FileHelpers for .Net はあなたの友達です。

于 2008-11-25T08:47:38.973 に答える
0

次のリンク「Regex fun with CSV」を参照してください。

http://snippets.dzone.com/posts/show/4430

于 2008-11-25T09:01:50.710 に答える
0

まあ、私は正規表現の達人ではありませんが、彼らはこれに対する答えを持っていると確信しています.

手続き的には、文字ごとに処理されます。dontMatch などの変数を FALSE に設定します。

見積もりに出くわすたびに、dontMatch を切り替えます。

コンマに遭遇するたびに、dontMatch をチェックしてください。TRUE の場合、カンマを無視します。FALSE の場合、カンマで分割します。

これはあなたが与えた例ではうまくいきますが、引用符に使用するロジックは根本的に間違っています - それらをエスケープするか、別の区切り文字 (たとえば、単一引用符) を使用して、主要な引用とマイナーな引用を区別する必要があります。

例えば、

"a", ""b", ""c", "d"", "e""

悪い結果になります。

これは、別のパッチで修正できます。単純に true false を保持するのではなく、引用符を一致させる必要があります。

引用符を一致させるには、最後に見たものを知る必要があり、これはかなり深い解析領域に入ります。おそらくその時点で、言語が適切に設計されていることを確認したいと思うでしょう。適切に設計されている場合は、コンパイラ ツールを使用してパーサーを作成できます。

-アダム

于 2008-11-25T08:15:18.633 に答える
0

コードで正規表現を試してみました.引用符付きの書式設定されたテキストに対しては正常に機能します...

しかし、正規表現で以下の値を解析できるかどうか疑問に思っています..

"First_Bat7679",""NAME","ENAME","FILE"","","","From: "DDD,_Ala%as"@sib.com"

私は次のような結果を探しています:

「First_Bat7679」
'"名前","ENAME","ファイル"'
''
''
「差出人: "DDD,_Ala%as"@sib.com」

ありがとう

于 2008-11-25T12:07:10.163 に答える