So teilen Sie CSV, deren Spalten enthalten können,

105

Gegeben

2,1016,7 / 31/2008 14: 22, Geoff Dalgas , 6/5/2011 22:21, http://stackoverflow.com , "Corvallis, OR", 7679,351,81, b437f461b3fd27387c5d8ab47a293d35,34

So verwenden Sie C #, um die obigen Informationen wie folgt in Zeichenfolgen aufzuteilen:

2
1016
7/31/2008 14:22
Geoff Dalgas
6/5/2011 22:21
http://stackoverflow.com
Corvallis, OR
7679
351
81
b437f461b3fd27387c5d8ab47a293d35
34

Wie Sie sehen können, enthält eine der Spalten <= (Corvallis, OR)

// update // Basierend auf C # Regex Split - Kommas außerhalb von Anführungszeichen

string[] result = Regex.Split(samplestring, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
q0987
quelle
1
Obwohl in Java, ähnliche Frage: stackoverflow.com/questions/1757065/…
sgokhales
1
Die Verwendung eines regulären Ausdrucks ist ein schlechter Rat. Das .NET Framework verfügt bereits über eine integrierte Unterstützung zum Parsen von CSV. Sehen Sie sich diese Antwort an, die Sie akzeptieren sollten. Andernfalls schließe ich dies als Betrug von stackoverflow.com/questions/3147836/…, was genauso falsch ist.
Kev
Können Sie bitte erläutern, welche Unterstützung .NET für das Parsen von CSV-Dateien mit eingebetteten Kommas bietet? Verweisen Sie auf die Microsoft.VisualBasic.FileIO.TextFieldParser-Klasse?
AllSolutions

Antworten:

182

Verwenden Sie die Microsoft.VisualBasic.FileIO.TextFieldParserKlasse. Dies behandelt das Parsen einer durch Trennzeichen getrennten Datei TextReaderoder Streamwenn einige Felder in Anführungszeichen eingeschlossen sind und andere nicht.

Beispielsweise:

using Microsoft.VisualBasic.FileIO;

string csv = "2,1016,7/31/2008 14:22,Geoff Dalgas,6/5/2011 22:21,http://stackoverflow.com,\"Corvallis, OR\",7679,351,81,b437f461b3fd27387c5d8ab47a293d35,34";

TextFieldParser parser = new TextFieldParser(new StringReader(csv));

// You can also read from a file
// TextFieldParser parser = new TextFieldParser("mycsvfile.csv");

parser.HasFieldsEnclosedInQuotes = true;
parser.SetDelimiters(",");

string[] fields;

while (!parser.EndOfData)
{
    fields = parser.ReadFields();
    foreach (string field in fields)
    {
        Console.WriteLine(field);
    }
} 

parser.Close();

Dies sollte zu folgender Ausgabe führen:

2
1016
31.07.2008 14:22
Geoff Dalgas
05.06.2011 22:21
http://stackoverflow.com
Corvallis, OR
7679
351
81
b437f461b3fd27387c5d8ab47a293d35
34

Weitere Informationen finden Sie unter Microsoft.VisualBasic.FileIO.TextFieldParser .

Sie müssen einen Verweis auf Microsoft.VisualBasicder Registerkarte Verweise hinzufügen .NET hinzufügen .

Tim
quelle
9
Alter, vielen Dank für diese Lösung. Ich habe mehr als 500.000 Zeilen CSV-Daten, die ich in eine Tabelle laden muss, und sie werden mit Kommas in Anführungszeichen geladen. Ich schulde Ihnen ein Getränk für Erwachsene Ihrer Wahl, falls sich unsere Wege jemals kreuzen.
Mark Kram
@tim Ich habe dies verwendet und bemerkt, dass alle geraden Zeilennummern übersprungen werden und nur die ungeraden Zeilennummern in einer Datei mit 1050 Zeilen verarbeitet werden. irgendwelche Ideen?
Smith
@Smith - Ohne Ihren Code oder Beispielbeispiel zu sehen, habe ich keine Ahnung. Ich schlage vor, eine neue Frage zu stellen. Möglicherweise fehlt in der Datei ein Wagenrücklauf oder eine andere Zeilenende-Markierung in den geraden Zeilen?
Tim
Ich wusste nicht einmal von dieser Bibliothek, bis ich das sah - danke! Wenn jemand anderes ein Beispiel möchte, das eine ganze CSV-Datei analysiert, lesen
Amy Barrett
2
Können wir Microsoft lynchen, weil es keinen Konstruktor bereitstellt, der eine Zeichenfolge benötigt, sodass wir zuerst durch den Rahmen der Konvertierung in einen Stream springen müssen? Ansonsten schöne Antwort.
Loren Pechtel
43

Es ist so viel spät, aber das kann für jemanden hilfreich sein. Wir können RegEx als Balg verwenden.

Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
String[] Fields = CSVParser.Split(Test);
Husen
quelle
4
Dies ist perfekt. Ich würde dies lieber verwenden, als eine ganze andere Bibliothek zu importieren. Bravo.
TheGeekYouNeed
1
Spielen asdf "" " wie ,\" df“,
Diese Lösung funktioniert nicht richtig - sie berücksichtigt keine Sprachmarken, was bedeutet, dass beim Lesen viele Sprachmarken an falschen Stellen vorhanden sind.
AidanH
Was ist, wenn das Endzitat in einer Zeile fehlt: asd, "", "as, \" df "," asd asd "," as
MarmiK
1
Dies funktionierte für mich und führte zu zitierten Sprachmarken. 30 Millionen Reihen davon. Sehr gut und eine minimale Menge an Code.
GBGOLC
4

Ich sehe, wenn Sie CSV-getrennten Text in Excel einfügen und einen "Text in Spalten" ausführen, werden Sie nach einem "Textqualifizierer" gefragt. Standardmäßig wird ein doppeltes Anführungszeichen verwendet, damit Text in doppelten Anführungszeichen als Literal behandelt wird. Ich stelle mir vor, dass Excel dies implementiert, indem es jeweils ein Zeichen nach dem anderen verwendet. Wenn es auf ein "Textqualifikationsmerkmal" stößt, geht es weiter zum nächsten "Qualifikationsmerkmal". Sie können dies wahrscheinlich selbst mit einer for-Schleife und einem Booleschen Wert implementieren, um anzugeben, ob Sie sich in wörtlichem Text befinden.

public string[] CsvParser(string csvText)
{
    List<string> tokens = new List<string>();

    int last = -1;
    int current = 0;
    bool inText = false;

    while(current < csvText.Length)
    {
        switch(csvText[current])
        {
            case '"':
                inText = !inText; break;
            case ',':
                if (!inText) 
                {
                    tokens.Add(csvText.Substring(last + 1, (current - last)).Trim(' ', ',')); 
                    last = current;
                }
                break;
            default:
                break;
        }
        current++;
    }

    if (last != csvText.Length - 1) 
    {
        tokens.Add(csvText.Substring(last+1).Trim());
    }

    return tokens.ToArray();
}
Roly
quelle
3

Verwenden Sie eine Bibliothek wie LumenWorks , um Ihre CSV-Lesung durchzuführen . Es verarbeitet Felder mit Anführungszeichen und ist wahrscheinlich insgesamt robuster als Ihre benutzerdefinierte Lösung, da es sie schon lange gibt.

Adam Lear
quelle
2

Es ist schwierig, CSV-Dateien zu analysieren, wenn die CSV-Datei entweder durch Kommas getrennte Zeichenfolgen, durch Kommas getrennte Zeichenfolgen in Anführungszeichen oder eine chaotische Kombination der beiden sein kann. Die von mir entwickelte Lösung ermöglicht eine der drei Möglichkeiten.

Ich habe eine Methode erstellt, ParseCsvRow (), die ein Array aus einer CSV-Zeichenfolge zurückgibt. Ich beschäftige mich zuerst mit doppelten Anführungszeichen in der Zeichenfolge, indem ich die Zeichenfolge in doppelte Anführungszeichen in ein Array namens quoteArray aufteile. CSV-Dateien mit Anführungszeichen sind nur gültig, wenn eine gerade Anzahl von doppelten Anführungszeichen vorhanden ist. Doppelte Anführungszeichen in einem Spaltenwert sollten durch ein Paar doppelter Anführungszeichen ersetzt werden (dies ist der Ansatz von Excel). Solange die CSV-Datei diese Anforderungen erfüllt, können Sie davon ausgehen, dass die Trennzeichen nur außerhalb von doppelten Anführungszeichen angezeigt werden. Kommas in doppelten Anführungszeichen sind Teil des Spaltenwerts und sollten beim Aufteilen der CSV-Datei in ein Array ignoriert werden.

Meine Methode prüft Kommas außerhalb von Paaren mit doppelten Anführungszeichen, indem sie nur gerade Indizes des Anführungszeichenarrays betrachtet. Außerdem werden doppelte Anführungszeichen am Anfang und am Ende von Spaltenwerten entfernt.

    public static string[] ParseCsvRow(string csvrow)
    {
        const string obscureCharacter = "ᖳ";
        if (csvrow.Contains(obscureCharacter)) throw new Exception("Error: csv row may not contain the " + obscureCharacter + " character");

        var unicodeSeparatedString = "";

        var quotesArray = csvrow.Split('"');  // Split string on double quote character
        if (quotesArray.Length > 1)
        {
            for (var i = 0; i < quotesArray.Length; i++)
            {
                // CSV must use double quotes to represent a quote inside a quoted cell
                // Quotes must be paired up
                // Test if a comma lays outside a pair of quotes.  If so, replace the comma with an obscure unicode character
                if (Math.Round(Math.Round((decimal) i/2)*2) == i)
                {
                    var s = quotesArray[i].Trim();
                    switch (s)
                    {
                        case ",":
                            quotesArray[i] = obscureCharacter;  // Change quoted comma seperated string to quoted "obscure character" seperated string
                            break;
                    }
                }
                // Build string and Replace quotes where quotes were expected.
                unicodeSeparatedString += (i > 0 ? "\"" : "") + quotesArray[i].Trim();
            }
        }
        else
        {
            // String does not have any pairs of double quotes.  It should be safe to just replace the commas with the obscure character
            unicodeSeparatedString = csvrow.Replace(",", obscureCharacter);
        }

        var csvRowArray = unicodeSeparatedString.Split(obscureCharacter[0]); 

        for (var i = 0; i < csvRowArray.Length; i++)
        {
            var s = csvRowArray[i].Trim();
            if (s.StartsWith("\"") && s.EndsWith("\""))
            {
                csvRowArray[i] = s.Length > 2 ? s.Substring(1, s.Length - 2) : "";  // Remove start and end quotes.
            }
        }

        return csvRowArray;
    }

Ein Nachteil meines Ansatzes ist die Art und Weise, wie ich Trennzeichen vorübergehend durch ein obskures Unicode-Zeichen ersetze. Dieses Zeichen muss so dunkel sein, dass es niemals in Ihrer CSV-Datei angezeigt wird. Möglicherweise möchten Sie dies besser behandeln.

Jason Williams
quelle
1

Ich hatte ein Problem mit einer CSV, die Felder mit einem Anführungszeichen enthält. Daher habe ich mit TextFieldParser Folgendes gefunden:

private static string[] parseCSVLine(string csvLine)
{
  using (TextFieldParser TFP = new TextFieldParser(new MemoryStream(Encoding.UTF8.GetBytes(csvLine))))
  {
    TFP.HasFieldsEnclosedInQuotes = true;
    TFP.SetDelimiters(",");

    try 
    {           
      return TFP.ReadFields();
    }
    catch (MalformedLineException)
    {
      StringBuilder m_sbLine = new StringBuilder();

      for (int i = 0; i < TFP.ErrorLine.Length; i++)
      {
        if (i > 0 && TFP.ErrorLine[i]== '"' &&(TFP.ErrorLine[i + 1] != ',' && TFP.ErrorLine[i - 1] != ','))
          m_sbLine.Append("\"\"");
        else
          m_sbLine.Append(TFP.ErrorLine[i]);
      }

      return parseCSVLine(m_sbLine.ToString());
    }
  }
}

Ein StreamReader wird weiterhin verwendet, um die CSV wie folgt zeilenweise zu lesen:

using(StreamReader SR = new StreamReader(FileName))
{
  while (SR.Peek() >-1)
    myStringArray = parseCSVLine(SR.ReadLine());
}
RooiWillie
quelle
1

Mit Cinchoo ETL - einer Open Source-Bibliothek - können Spaltenwerte , die Trennzeichen enthalten, automatisch verarbeitet werden.

string csv = @"2,1016,7/31/2008 14:22,Geoff Dalgas,6/5/2011 22:21,http://stackoverflow.com,""Corvallis, OR"",7679,351,81,b437f461b3fd27387c5d8ab47a293d35,34";

using (var p = ChoCSVReader.LoadText(csv)
    )
{
    Console.WriteLine(p.Dump());
}

Ausgabe:

Key: Column1 [Type: String]
Value: 2
Key: Column2 [Type: String]
Value: 1016
Key: Column3 [Type: String]
Value: 7/31/2008 14:22
Key: Column4 [Type: String]
Value: Geoff Dalgas
Key: Column5 [Type: String]
Value: 6/5/2011 22:21
Key: Column6 [Type: String]
Value: http://stackoverflow.com
Key: Column7 [Type: String]
Value: Corvallis, OR
Key: Column8 [Type: String]
Value: 7679
Key: Column9 [Type: String]
Value: 351
Key: Column10 [Type: String]
Value: 81
Key: Column11 [Type: String]
Value: b437f461b3fd27387c5d8ab47a293d35
Key: Column12 [Type: String]
Value: 34

Weitere Informationen finden Sie im Artikel zum Codeprojekt.

Ich hoffe es hilft.

RajN
quelle