webdevqa.jp.net

.NETで区切りCSVを解析する

ほとんどのフィールドで"で区切られたコンマ区切り形式のテキストファイルがあります。私はそれを列挙可能なものにしようとしています(たとえば、ジェネリックコレクション)。ファイルの出力方法や区切り文字に使用する文字を制御することはできません。

この場合、フィールドはコンマで区切られ、テキストフィールドは"マークで囲まれます。私が遭遇している問題は、いくつかのフィールドに引用符があり(つまり、8" Tray)、誤って次のフィールドとして選択されていることです。数値フィールドの場合、それらは引用符で囲まれていませんが、+または-記号(正/負の数を示す)で始まります。

私は正規表現を考えていましたが、私のスキルはそれほど素晴らしいものではないので、誰かが私が試すことができるいくつかのアイデアを思い付くことができれば幸いです。このファイルには約19,000件のレコードが含まれているため、できる限り効率的に実行しようとしています。以下に、データの行の例をいくつか示します。

"00","000000112260   ","Pie Pumpkin                             ","RET","6.99 ","     ","ea ",+0000000006.99000
"00","000000304078   ","Pie Apple caramel                       ","RET","9.99 ","     ","ea ",+0000000009.99000
"00","StringValue here","8" Tray of Food                             ","RET","6.99 ","     ","ea ",-00000000005.3200

さらに多くのフィールドがありますが、画像を取得できます。

VB.NETを使用していますが、データを受け入れるための一般的なリストのセットアップがあります。 CSVReader を使用してみましたが、3番目のようなレコード(テキストフィールドに引用符が付いている)に到達するまではうまくいくようです。どうにかして追加の引用符を処理できるようになれば、CSVReaderオプションはうまく機能します。

ありがとう!

24
hacker

ここ から:

Encoding fileEncoding = GetFileEncoding(csvFile);
// get rid of all doublequotes except those used as field delimiters
string fileContents = File.ReadAllText(csvFile, fileEncoding);
string fixedContents = Regex.Replace(fileContents, @"([^\^,\r\n])""([^$,\r\n])", @"$1$2");
using (CsvReader csv =
       new CsvReader(new StringReader(fixedContents), true))
{
       // ... parse the CSV
7
Mitch Wheat

.Netの TextFieldParserClass を確認することをお勧めします。含める必要があります

Imports Microsoft.VisualBasic.FileIO.TextFieldParser

以下に簡単なサンプルを示します。

        Dim afile As FileIO.TextFieldParser = New FileIO.TextFieldParser(FileName)
        Dim CurrentRecord As String() ' this array will hold each line of data
        afile.TextFieldType = FileIO.FieldType.Delimited
        afile.Delimiters = New String() {","}
        afile.HasFieldsEnclosedInQuotes = True

        ' parse the actual file
        Do While Not afile.EndOfData
            Try
                CurrentRecord = afile.ReadFields
            Catch ex As FileIO.MalformedLineException
                Stop
            End Try
        Loop
76
Avi

このサイトを試してください。 http://kbcsv.codeplex.com/

私は優れたユーティリティを探しましたが、これは私が見つけた最高のものであり、正しく動作します。他のものを試して時間を無駄にしないでください、これは無料で動作します。

11
Middletone

このリンクが言うように... 独自のCSVパーサーをロールしないでください!

Aviが推奨するようにTextFieldParserを使用します。 Microsoftは既にこれを行っています。最終的に1つを書き、その中にバグを見つけた場合は、バグを修正するのではなく、それを置き換えることを検討してください。最近それをやったので、時間を大幅に節約できました。

7
stone

FileHelpersライブラリ を見てください。

5
CMS

CsvHelper (私が管理しているライブラリ)を試してみると、 NuGet で利用できます。 RFC 418 CSVの標準に従います。コンマ、引用符、改行を含むフィールド内のコンテンツを処理できます。

CsvHelperは簡単に使用できますが、さまざまな種類の区切りファイルで動作するように構成するのも簡単です。

CsvReader csv = new CsvReader( streamToFile );
IEnumerable<MyObject> myObjects = csv.GetRecords<MyObject>();

下位レベルのCSVファイルを読み取りたい場合は、パーサーを直接使用できます。これにより、各行が文字列配列として返されます。

var parser = new CsvParser( myTextReader );
while( true )
{
    string[] line = parser.ReadLine();
    if( line == null )
    {
        break;
    }
}
5
Josh Close

私はこれを答えとして投稿しているので、どうやってそれをやったのかを説明できます.... Mitch Wheatからの答えは、このケースに最適なソリューションを提供してくれました。このデータはエクスポートされました。

VBコード:

Dim fixedContents As String = Regex.Replace(
                            File.ReadAllText(csvFile, fileEncoding),
                            "(?<!,)("")(?!,)", 
                            AddressOf ReplaceQuotes)

使用されたRegExは、特定のフィールドにエスケープされていない引用符が含まれていて、提供されたRegExがすべての例で機能しなかったため、変更する必要がありました。これは、「先を見る」と「後ろを見る」を使用して、引用符がコンマの直後か直前かを確認します。この場合、両方とも負です(つまり、二重引用符がコンマの前後にない場合に表示されます)。これは、引用符が文字列の途中にあることを意味するはずです。

この場合、直接の置き換えを行う代わりに、ReplaceQuotes関数を使用して処理します。これを使用している理由は、行の先頭にあるかどうかを検出するために少し余分なロジックが必要だったからです。さらに時間を費やしていた場合は、RegExを調整して行の先頭を考慮に入れることができたはずです(MultiLineなどを使用)が、すぐに試してみると、すべて。

32MB CSVファイル(約19000行)でCSVリーダーを使用すると、ファイルの読み取り、正規表現の実行、CSVリーダーへの読み込み、すべてのデータの汎用クラスへの追加、完了に約2秒かかります。本当に速い!!

1
hacker

最初と最後の引用符を除外する正規表現は(?<!^)(?<!,)("")(?!,)(?!$)。もちろん、RegexOptions.Multilineを使用する必要があります。

そうすれば、評価機能は必要ありません。私のコードは、望ましくない二重引用符を単一引用符に置き換えます。

完全なC#コードは次のとおりです。

string fixedCSV = Regex.Replace(
            File.ReadAllText(fileName),
            @"(?<!^)(?<!;)("")(?!;)(?!$)", "'", RegexOptions.Multiline);
1
mariob

CSVファイルには少なくともODBCドライバーがあります。ただし、CSVにはさまざまな種類があります。

これらのファイルを作成したのは何ですか?ソースアプリケーションの要件に基づいて一致するドライバーが存在する可能性は低くありません。

0
dkretz
        public static Encoding GetFileEncoding(String fileName)
    {
        Encoding Result = null;
        FileInfo FI = new FileInfo(fileName);
        FileStream FS = null;

        try
        {
            FS = FI.OpenRead();
            Encoding[] UnicodeEncodings = { Encoding.BigEndianUnicode, Encoding.Unicode, Encoding.UTF8 };
            for (int i = 0; Result == null && i < UnicodeEncodings.Length; i++)
            {
                FS.Position = 0;
                byte[] Preamble = UnicodeEncodings[i].GetPreamble();
                bool PreamblesAreEqual = true;
                for (int j = 0; PreamblesAreEqual && j < Preamble.Length; j++)
                {
                    PreamblesAreEqual = Preamble[j] == FS.ReadByte();
                }
                if (PreamblesAreEqual)
                {
                    Result = UnicodeEncodings[i];
                }
            }
        }
        catch (System.IO.IOException)
        {
        }
        finally
        {
            if (FS != null)
            {
                FS.Close();
            }
        }

        if (Result == null)
        {
            Result = Encoding.Default;
        }

        return Result;
    }
0
Daver

CSVReaderの問題は、3番目のレコードの引用符が別の引用符(別名二重引用符)でエスケープされないことです。それらをエスケープしない場合、テキストフィールドの中央で ""をどのように処理しますか?

http://en.wikipedia.org/wiki/Comma-separated_values

(結局、(異なる区切り文字を使用した)ファイルを処理する必要がありましたが、テキスト値内の引用文字はエスケープされず、独自のカスタムパーサーを記述しました。これが絶対に必要かどうかはわかりません。)

0
llamaoo7

このカスタムアプローチのロジックは、ファイルを1行ずつ読み取り、各行をコンマで分割し、最初と最後の文字を削除し(外側の引用符を削除し、内側の引用符には影響を与えません)、データをジェネリックに追加しますリスト。短くて読みやすく、操作も簡単です。

        Dim fr As StreamReader = Nothing
        Dim FileString As String = ""
        Dim LineItemsArr() as String

        Dim FilePath As String = HttpContext.Current.Request.MapPath("YourFile.csv")

        fr = New System.IO.StreamReader(FilePath)

        While fr.Peek <> -1
            FileString = fr.ReadLine.Trim

            If String.IsNullOrEmpty(FileString) Then Continue While 'Empty Line

            LineItemsArr = FileString.Split(",")

            For Each Item as String In LineItemsArr
                'If every item will have a beginning and closing " (quote) then you can just
                'cut the first and last characters of the string here.
                'i.e.  UpdatedItems = Item. remove first and last character

                'Then stick the data into your Generic List (Of String()?)
            Next
        End While
0
rvarcher