none
CSV mit TextfieldParser lesen RRS feed

  • Frage

  • Hallo,

    ich lese CSV-Dateien mit dem Microsoft.VisualBasic.FileIO.TextFieldParser ein. Bisher hat alles wunderbar funktioniert.

    Nun gibt es ein Problem, wenn in einem Feld Anführungszeichen vorkommen:

    23,Vorname,"Aus dem Magazin \"schön programmieren\"", Nachname

    In diesem Fall wird die nachstehende Exception geworfen:

    Microsoft.VisualBasic.FileIO.MalformedLineException: Die Zeile 2 kann mit den aktuellen Trennzeichen nicht analysiert werden.
       bei Microsoft.VisualBasic.FileIO.TextFieldParser.ParseDelimitedLine()

    Ersetze ich \" mit einem Hochkomme ' gibt es keine Probleme. Habt Ihr eine Idee, wie ich solche Fälle behandeln kann?

    Danke und Grüße,
    Christian

    Dienstag, 30. September 2014 18:22

Antworten

  • Hallo,
    ich würde (besonders unter C#) nicht auf den VB Namespace vertrauen. Den gibt es so nur noch am Desktop und dort auch nur noch aus Kompatibilitätsgründen.

    Das Parsen an sich funktioniert auch über LINQ extrem einfach. Ein Beispiel dazu:

    var rows = File.ReadLines("<DATEINAME>").Select(x=>x.Split('<SPLITCHAR>')).ToArray();

    <SPLITCHAR> ist dabei ndas Trennzeichen der einzelnen Spalten im Datensatz.

    Nun musst du nurnoch die einzelnen Zeilen und dann die jeweile Spalte per Index auswählen:

    var value = rows[ZEILENINDEX][SPALTENINDEX];
    PS: Wenn du alle Datensätze in einer foreach-Schleife durchlaufen willst, solltest du das ToArray am Ende der Abfrage weg lassen. Dadurch wird die Datei Zeile für Zeile gelesen, was bei großen Dateien effizienter ist.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Dienstag, 30. September 2014 18:35
    Moderator
  • Hallo Christian,

    ich schließe mich da Tom an. Es gibt aber falls du LINQ nicht verwenden möchtest oder kannst mind. eine weitere Möglichkeit. Du kann einen StreamReader nutzen, die Datei Zeilenweise einlesen und die Zeile nach den Feldern splitten. Der Fehler tritt dabei nicht auf und diese Methode ist um einiges schneller als per TextFieldParser.

    char[] delimiters = new char[] { ',' };
    using (StreamReader reader = new StreamReader("C:\\test.csv"))
    	{
                    while (true)
                    {
                        string line = reader.ReadLine();
                        if (line == null)
                        {
                            break;
                        }
                        string[] parts = line.Split(delimiters);
                        Console.WriteLine("{0} field(s)", parts.Length);
                    }
    	}

    Gruß


    Jens Gerber

    Dienstag, 30. September 2014 18:49
  • Hallo Christian,

    zu dem bereits von anderen Gesagten:

    Umgehen kann der TextFieldParser "nur" mit gedoppelten Anführungszeichen, so würde z. B. funktionieren:

    21,Vorname,"Aus dem Magazin ""schön programmieren""", Nachname

    Was ein "üblicheres" Format ist, weil es u. a. bei Textdateien von Microsoft Access (uam.) verwendet wird.

    Ein Escape via Backslash wie es C# (und andere Sprachen) kennt, unterstützt er nicht  - der Text-Import von Access übrigens auch nicht - insofern ist Dein CSV Format etwas zu eigen.

    Gruß Elmar

    Dienstag, 30. September 2014 19:10
    Beantworter

Alle Antworten

  • Hallo,
    ich würde (besonders unter C#) nicht auf den VB Namespace vertrauen. Den gibt es so nur noch am Desktop und dort auch nur noch aus Kompatibilitätsgründen.

    Das Parsen an sich funktioniert auch über LINQ extrem einfach. Ein Beispiel dazu:

    var rows = File.ReadLines("<DATEINAME>").Select(x=>x.Split('<SPLITCHAR>')).ToArray();

    <SPLITCHAR> ist dabei ndas Trennzeichen der einzelnen Spalten im Datensatz.

    Nun musst du nurnoch die einzelnen Zeilen und dann die jeweile Spalte per Index auswählen:

    var value = rows[ZEILENINDEX][SPALTENINDEX];
    PS: Wenn du alle Datensätze in einer foreach-Schleife durchlaufen willst, solltest du das ToArray am Ende der Abfrage weg lassen. Dadurch wird die Datei Zeile für Zeile gelesen, was bei großen Dateien effizienter ist.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Dienstag, 30. September 2014 18:35
    Moderator
  • Hallo Christian,

    ich schließe mich da Tom an. Es gibt aber falls du LINQ nicht verwenden möchtest oder kannst mind. eine weitere Möglichkeit. Du kann einen StreamReader nutzen, die Datei Zeilenweise einlesen und die Zeile nach den Feldern splitten. Der Fehler tritt dabei nicht auf und diese Methode ist um einiges schneller als per TextFieldParser.

    char[] delimiters = new char[] { ',' };
    using (StreamReader reader = new StreamReader("C:\\test.csv"))
    	{
                    while (true)
                    {
                        string line = reader.ReadLine();
                        if (line == null)
                        {
                            break;
                        }
                        string[] parts = line.Split(delimiters);
                        Console.WriteLine("{0} field(s)", parts.Length);
                    }
    	}

    Gruß


    Jens Gerber

    Dienstag, 30. September 2014 18:49
  • Hallo Tom,

    vielen Dank für Deine Hilfe!

    Mit Zeilenumbrüchen innerhalb einer Spalte hätte ich aber vermutlich Probleme, oder?

    Viele Grüße,
    Christian

    Dienstag, 30. September 2014 19:02
  • Hallo Jens,

    vielen Dank auch für Deine Hilfe!

    Aber auch hier hätte ich vermutlich Probleme bei Zeilenumbrüchen, oder?

    Viele Grüße,
    Christian

    Dienstag, 30. September 2014 19:04
  • Ja, in dem Fall müsstest du den String zunächst an einem anderen Zeichen aufspalten.

    var rows = File.ReadAllText.Split(';').Select(x=>x.Split(',')).ToArray();
    //Statt Zeilenumbruch werden Datensätze durch ';' getrennt
    //Datenfelder durch ','

    CSV Dateien dürfen an sich weder Datensatztrennzeichen (normalerweise Zeilenumbrüche), noch Trennzeichen (oftmals Semikolons) in den Werten enthalten.
    Welche Zeichen verwendet werden, macht beim parsen aber kaum einen Unterschied. Auch wenn es mit Zeilenumbrüchen am einfachsten ist.


    Tom Lambert - C# MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Dienstag, 30. September 2014 19:07
    Moderator
  • Hallo Christian,

    zu dem bereits von anderen Gesagten:

    Umgehen kann der TextFieldParser "nur" mit gedoppelten Anführungszeichen, so würde z. B. funktionieren:

    21,Vorname,"Aus dem Magazin ""schön programmieren""", Nachname

    Was ein "üblicheres" Format ist, weil es u. a. bei Textdateien von Microsoft Access (uam.) verwendet wird.

    Ein Escape via Backslash wie es C# (und andere Sprachen) kennt, unterstützt er nicht  - der Text-Import von Access übrigens auch nicht - insofern ist Dein CSV Format etwas zu eigen.

    Gruß Elmar

    Dienstag, 30. September 2014 19:10
    Beantworter
  • Hallo Christian,

    ja hättest du. In meinem Beispiel entspricht ein Zeilenumbruch einem neuen Datensatz. In dem LINQ Beispiel von Tom scheint man das umgehen zu können (aus meiner Sicht ist das dann aber keine konsistente CSV Datei mehr). Ich würde heutzutage (außer es geht nicht anders bspw. weil es eine Schnittstelle zu einem anderen Programm ist) keine CSV Dateien für irgendwelche Exporte oder Importe nutzen. Mit XML hat man da wesentlich weniger Probleme.

    Gruß


    Jens Gerber

    Dienstag, 30. September 2014 19:19