Analysieren von CSV-Dateien in C # mit Header

264

Gibt es eine standardmäßige / offizielle / empfohlene Methode zum Parsen von CSV-Dateien in C #? Ich möchte meinen eigenen Parser nicht rollen.

Außerdem habe ich Fälle von Personen gesehen, die ODBC / OLE DB zum Lesen von CSV über den Texttreiber verwenden, und viele Personen raten aufgrund seiner "Nachteile" davon ab. Was sind diese Nachteile?

Im Idealfall suche ich nach einer Möglichkeit, die CSV anhand des Spaltennamens zu lesen, wobei der erste Datensatz als Kopf- / Feldname verwendet wird. Einige der angegebenen Antworten sind korrekt, arbeiten jedoch daran, die Datei grundsätzlich in Klassen zu deserialisieren.

David Pfeffer
quelle

Antworten:

138

Lassen Sie eine Bibliothek alle wichtigen Details für Sie erledigen! :-)

Schauen Sie sich FileHelpers an und bleiben Sie trocken - wiederholen Sie sich nicht - Sie müssen das Rad nicht ein millionenfaches Mal neu erfinden ...

Grundsätzlich müssen Sie nur diese Form Ihrer Daten - die Felder in Ihrer einzelnen Zeile in der CSV - mithilfe einer öffentlichen Klasse (und so gut durchdachter Attribute wie Standardwerte, Ersetzungen für NULL-Werte usw.) definieren Die FileHelpers-Engine für eine Datei und Bingo - Sie erhalten alle Einträge aus dieser Datei zurück. Eine einfache Operation - tolle Leistung!

marc_s
quelle
1
Bis Sie etwas wirklich Benutzerdefiniertes benötigen (und das meiste davon kann sowieso als Erweiterung implementiert werden), ist FileHelpers bei weitem der beste Weg, eine wirklich bequeme, getestete und leistungsfähige Lösung
mikus
3
Ab dem 1. Juni 2015 konnte ich FileHelpers nur herunterladen, indem ich auf sourceforge.net danach suchte. Hier ist der verwendete Link: sourceforge.net/projects/filehelpers/?source=directory
Sudhanshu Mishra
2
@dotnetguy wir sind im Weg zu Release 3.1 (derzeit 3.1-rc2) ist raus. Außerdem haben wir die Seite neu gestaltet: www.filehelpers.net Sie können die neueste Version von dort herunterladen
Marcos Meli
1
@ MarcosMeli vielen Dank! Ich habe FileHelpers bereits in einem meiner Projekte verwendet und es war ein Kinderspiel - ein großes Lob an das Team. Ich plane bald einen Blog darüber und übrigens - Ich liebe die neue Seite - gut gemacht!
Sudhanshu Mishra
FileHelpers verarbeitet Kommas in Anführungszeichen in CSVs nicht richtig oder ordnet Feldüberschriften tatsächlich zu. Stattdessen wird erwartet, dass die Spalten in derselben Reihenfolge vorliegen, in der die Felder in Ihrem Typ deklariert sind. Ich würde es persönlich nicht benutzen.
Alastair Maw
356

Ein CSV-Parser ist jetzt Teil von .NET Framework.

Fügen Sie einen Verweis auf Microsoft.VisualBasic.dll hinzu (funktioniert in C # einwandfrei, der Name spielt keine Rolle)

using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    while (!parser.EndOfData)
    {
        //Process row
        string[] fields = parser.ReadFields();
        foreach (string field in fields)
        {
            //TODO: Process field
        }
    }
}

Die Dokumente sind hier - TextFieldParser-Klasse

PS Wenn Sie einen CSV- Exporteur benötigen , versuchen Sie es mit CsvExport (siehe unten: Ich bin einer der Mitwirkenden).

Alex
quelle
2
Nach meiner Erfahrung funktioniert TextFieldParser mit großen Dateien (z. B.> 250 MB) nicht gut. :(
MBoros
6
TextFieldParser implementiert IDisposable. Verwenden Sie es daher am besten in einer using-Klausel. Ansonsten gute Antwort.
Chris Bush
3
Im Konstruktor möchten Sie möglicherweise eine andere Codierung als die standardmäßig verwenden, z. B.: New TextFieldParser ("c: \ temp \ test.csv", System.Text.Encoding.UTF8)
neural5torm
1
Beachten Sie, dass Felder in Ihrer CSV, die Leerzeilen enthalten, übersprungen werden TextFieldParser.ReadLine(). Siehe TextFieldParser docs
mcNux
3
Gibt es eine Möglichkeit, dies in .NET Core zu erreichen?
Hugo Zink
183

CsvHelper (eine von mir verwaltete Bibliothek) liest eine CSV-Datei in benutzerdefinierte Objekte.

var csv = new CsvReader( File.OpenText( "file.csv" ) );
var myCustomObjects = csv.GetRecords<MyCustomObject>();

Manchmal besitzen Sie nicht die Objekte, in die Sie lesen möchten. In diesem Fall können Sie eine fließende Zuordnung verwenden, da Sie der Klasse keine Attribute zuweisen können.

public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
    public MyCustomObjectMap()
    {
        Map( m => m.Property1 ).Name( "Column Name" );
        Map( m => m.Property2 ).Index( 4 );
        Map( m => m.Property3 ).Ignore();
        Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
    }
}

BEARBEITEN:

Für CsvReader muss jetzt CultureInfo an den Konstruktor übergeben werden ( https://github.com/JoshClose/CsvHelper/issues/1441 ).

Beispiel:

var csv = new CsvReader(File.OpenText("file.csv"), System.Globalization.CultureInfo.CurrentCulture);
Josh Close
quelle
18
Ich stimme @ kubal5003 zu. Was mich daran verkauft hat, war, dass Sie es als NuGet-Paket zur Verfügung haben. Danke Mann, es ist schnell und macht alle CSV-Lesungen, die ich brauche.
Gromer
7
Es ist verdammt schnell. 1,3 Millionen Datensätze wurden in 10 Sekunden gelesen und deserialisiert.
Marisks
2
Tolle Bibliothek sehr einfach zu implementieren. Ich würde Josh nur vorschlagen, seine Antwort hier zu aktualisieren, da sich die Bibliothek seit dem Schreiben dieser Antwort etwas geändert hat und Sie CsvHelper nicht mehr instanziieren können (es ist jetzt nur ein Namespace), aber Sie müssen die CsvReader-Klasse verwenden.
Marko
1
CsvClassMap scheint in der letzten Version von CsvHelper nicht zu existieren?
Knocte
1
knocte, es heißt jetzt ClassMap. Es gibt auch andere Änderungen, z. B. das Lesen, bevor Sie nach dem Header-Datensatz fragen (der übrigens auf den Wert gesetzt wird, der beim ersten Aufruf von Read () gelesen wurde). Wie andere bereits erwähnt haben, ist es superschnell und einfach zu handhaben.
Norgie
31

In einer Geschäftsanwendung verwende ich das Open Source-Projekt auf codeproject.com, CSVReader .

Es funktioniert gut und hat eine gute Leistung. Es gibt einige Benchmarking für den Link, den ich bereitgestellt habe.

Ein einfaches Beispiel, kopiert von der Projektseite:

using (CsvReader csv = new CsvReader(new StreamReader("data.csv"), true))
{
    int fieldCount = csv.FieldCount;
    string[] headers = csv.GetFieldHeaders();

    while (csv.ReadNextRecord())
    {
        for (int i = 0; i < fieldCount; i++)
            Console.Write(string.Format("{0} = {1};", headers[i], csv[i]));

        Console.WriteLine();
    }
}

Wie Sie sehen können, ist es sehr einfach, damit zu arbeiten.

alexn
quelle
12

Wenn Sie nur CSV- Dateien lesen müssen, empfehle ich diese Bibliothek: Ein schneller CSV-Reader
Wenn Sie auch CSV- Dateien generieren müssen, verwenden Sie diese: FileHelpers

Beide sind kostenlos und OpenSource.

Giorgi
quelle
FileHelpers hat eine ansprechende Zusammenfassung: filehelpers.com Die FileHelpers sind eine kostenlose und einfach zu verwendende .NET-Bibliothek zum Importieren / Exportieren von Daten aus festen oder begrenzten Datensätzen in Dateien, Zeichenfolgen oder Streams.
AnneTheAgile
Während dieser Link möglicherweise die Frage beantwortet, werden bei Stack Overflow von Antworten nur auf Links abgeraten. Sie können diese Antwort verbessern, indem Sie wichtige Teile des Links in Ihre Antwort aufnehmen. Dadurch wird sichergestellt, dass Ihre Antwort immer noch eine Antwort ist, wenn der Link geändert wird oder entfernt :)
WhatsThePoint
11

Hier ist eine Hilfsklasse, die ich oft benutze, falls jemand jemals auf diesen Thread zurückkommt (ich wollte ihn teilen).

Ich benutze dies, um es einfach in gebrauchsfertige Projekte zu portieren:

public class CSVHelper : List<string[]>
{
  protected string csv = string.Empty;
  protected string separator = ",";

  public CSVHelper(string csv, string separator = "\",\"")
  {
    this.csv = csv;
    this.separator = separator;

    foreach (string line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !string.IsNullOrEmpty(s)))
    {
      string[] values = Regex.Split(line, separator);

      for (int i = 0; i < values.Length; i++)
      {
        //Trim values
        values[i] = values[i].Trim('\"');
      }

      this.Add(values);
    }
  }
}

Und benutze es wie:

public List<Person> GetPeople(string csvContent)
{
  List<Person> people = new List<Person>();
  CSVHelper csv = new CSVHelper(csvContent);
  foreach(string[] line in csv)
  {
    Person person = new Person();
    person.Name = line[0];
    person.TelephoneNo = line[1];
    people.Add(person);
  }
  return people;
}

[Aktualisierter CSV-Helfer: Fehler behoben, bei dem das letzte neue Zeilenzeichen eine neue Zeile erstellt hat]

Base33
quelle
17
Wenn einer der CSV-Einträge Komma (,) enthält, funktioniert dieser Code nicht.
Hakan
Um die Dinge leicht zu halten, habe ich einen Pfeifencharakter als Trennzeichen verwendet. '|'
Base33
ausgezeichnete Lösung. Nur eine Frage zum 2. Snippet. Was für ein Objekt ist Person
Cocoa Dev
@CocoaDev Es ist eine Klasse, die zwei Zeichenfolgeneigenschaften enthält - Name und TelephoneNo. Nur für das Beispiel. Wenn eine der Eigenschaften eine Ganzzahl war, sollte es sich nur um eine einfache Konvertierung handeln (mit Prüfung?).
Base33
10

Diese Lösung verwendet die offizielle Microsoft.VisualBasic- Assembly zum Parsen von CSV.

Vorteile:

  • Trennzeichen entkommt
  • ignoriert Header
  • Trimmräume
  • Kommentare ignorieren

Code:

    using Microsoft.VisualBasic.FileIO;

    public static List<List<string>> ParseCSV (string csv)
    {
        List<List<string>> result = new List<List<string>>();


        // To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project. 
        using (TextFieldParser parser = new TextFieldParser(new StringReader(csv))) 
        {
            parser.CommentTokens = new string[] { "#" };
            parser.SetDelimiters(new string[] { ";" });
            parser.HasFieldsEnclosedInQuotes = true;

            // Skip over header line.
            //parser.ReadLine();

            while (!parser.EndOfData)
            {
                var values = new List<string>();

                var readFields = parser.ReadFields();
                if (readFields != null)
                    values.AddRange(readFields);
                result.Add(values);
            }
        }

        return result;
    }
Jonas_Hess
quelle
7

Ich habe TinyCsvParser für .NET geschrieben, einen der schnellsten .NET-Parser, der es gibt und der in hohem Maße konfigurierbar ist, um fast jedes CSV-Format zu analysieren.

Es wird unter MIT-Lizenz veröffentlicht:

Sie können NuGet verwenden , um es zu installieren. Führen Sie den folgenden Befehl in der Package Manager-Konsole aus .

PM> Install-Package TinyCsvParser

Verwendung

Stellen Sie sich vor, wir haben eine Liste von Personen in einer CSV-Datei persons.csvmit Vorname, Nachname und Geburtsdatum.

FirstName;LastName;BirthDate
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02

Das entsprechende Domänenmodell in unserem System könnte folgendermaßen aussehen.

private class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

Wenn Sie TinyCsvParser verwenden, müssen Sie die Zuordnung zwischen den Spalten in den CSV-Daten und der Eigenschaft in Ihrem Domänenmodell definieren.

private class CsvPersonMapping : CsvMapping<Person>
{

    public CsvPersonMapping()
        : base()
    {
        MapProperty(0, x => x.FirstName);
        MapProperty(1, x => x.LastName);
        MapProperty(2, x => x.BirthDate);
    }
}

Und dann können wir das Mapping verwenden, um die CSV-Daten mit a zu analysieren CsvParser.

namespace TinyCsvParser.Test
{
    [TestFixture]
    public class TinyCsvParserTest
    {
        [Test]
        public void TinyCsvTest()
        {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
            CsvPersonMapping csvMapper = new CsvPersonMapping();
            CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);

            var result = csvParser
                .ReadFromFile(@"persons.csv", Encoding.ASCII)
                .ToList();

            Assert.AreEqual(2, result.Count);

            Assert.IsTrue(result.All(x => x.IsValid));

            Assert.AreEqual("Philipp", result[0].Result.FirstName);
            Assert.AreEqual("Wagner", result[0].Result.LastName);

            Assert.AreEqual(1986, result[0].Result.BirthDate.Year);
            Assert.AreEqual(5, result[0].Result.BirthDate.Month);
            Assert.AreEqual(12, result[0].Result.BirthDate.Day);

            Assert.AreEqual("Max", result[1].Result.FirstName);
            Assert.AreEqual("Mustermann", result[1].Result.LastName);

            Assert.AreEqual(2014, result[1].Result.BirthDate.Year);
            Assert.AreEqual(1, result[1].Result.BirthDate.Month);
            Assert.AreEqual(1, result[1].Result.BirthDate.Day);
        }
    }
}

Benutzerhandbuch

Ein vollständiges Benutzerhandbuch finden Sie unter:

Bytefisch
quelle
1

Hier ist meine KISS-Implementierung ...

using System;
using System.Collections.Generic;
using System.Text;

class CsvParser
{
    public static List<string> Parse(string line)
    {
        const char escapeChar = '"';
        const char splitChar = ',';
        bool inEscape = false;
        bool priorEscape = false;

        List<string> result = new List<string>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < line.Length; i++)
        {
            char c = line[i];
            switch (c)
            {
                case escapeChar:
                    if (!inEscape)
                        inEscape = true;
                    else
                    {
                        if (!priorEscape)
                        {
                            if (i + 1 < line.Length && line[i + 1] == escapeChar)
                                priorEscape = true;
                            else
                                inEscape = false;
                        }
                        else
                        {
                            sb.Append(c);
                            priorEscape = false;
                        }
                    }
                    break;
                case splitChar:
                    if (inEscape) //if in escape
                        sb.Append(c);
                    else
                    {
                        result.Add(sb.ToString());
                        sb.Length = 0;
                    }
                    break;
                default:
                    sb.Append(c);
                    break;
            }
        }

        if (sb.Length > 0)
            result.Add(sb.ToString());

        return result;
    }

}
Alex begann
quelle
1
Dies gilt nicht für Zeilenumbrüche in Zeichenfolgen in Anführungszeichen, die in einer CSV-Datei gültig sind.
John Leidegren
Alex, John versucht zu sagen, dass RFC 4180 ( ietf.org/rfc/rfc4180.txt - Siehe Abschnitt 2 und Punkt 6) es einer Spalte ermöglicht, einen CR LF in der Mitte einer Spalte zu haben, die ihn effektiv verteilt 2 Zeilen in einer Datei. Ihre Lösung wird wahrscheinlich in den meisten Fällen gut funktionieren (insbesondere wenn die CSV-Dateien durch Speichern aus Excel erstellt wurden), deckt diesen Randfall jedoch nicht ab. Der oben erwähnte CsvHelper soll diesen Fall berücksichtigen.
David Yates
Ja, das stimmt, aber wenn Sie CR LF in Ihrer CSV haben, sollten Sie wahrscheinlich nicht CSV verwenden, sondern etwas passenderes wie json oder xml oder ein Format mit fester Länge.
Alex begann am
1

Vor einiger Zeit hatte ich eine einfache Klasse für CSV-Lesen / Schreiben basierend auf der Microsoft.VisualBasicBibliothek geschrieben. Mit dieser einfachen Klasse können Sie mit CSV wie mit einem zweidimensionalen Array arbeiten. Sie finden meine Klasse unter folgendem Link: https://github.com/ukushu/DataExporter

Einfaches Anwendungsbeispiel:

Csv csv = new Csv("\t");//delimiter symbol

csv.FileOpen("c:\\file1.csv");

var row1Cell6Value = csv.Rows[0][5];

csv.AddRow("asdf","asdffffff","5")

csv.FileSave("c:\\file2.csv");

Zum Lesen des Headers müssen Sie nur die csv.Rows[0]Zellen lesen :)

Andrew
quelle
1

Single-Source-Dateilösung für einfache Parsing-Anforderungen, nützlich. Beschäftigt sich mit allen bösen Randfällen. B. die Normalisierung neuer Zeilen und die Behandlung neuer Zeilen in Zeichenfolgenliteralen in Anführungszeichen. Herzlich willkommen!

Wenn Ihre CSV-Datei einen Header hat, lesen Sie einfach die Spaltennamen (und berechnen die Spaltenindizes) aus der ersten Zeile. So einfach ist das.

Beachten Sie, dass Dumpes sich um eine LINQPad-Methode handelt. Sie können diese entfernen, wenn Sie LINQPad nicht verwenden.

void Main()
{
    var file1 = "a,b,c\r\nx,y,z";
    CSV.ParseText(file1).Dump();

    var file2 = "a,\"b\",c\r\nx,\"y,z\"";
    CSV.ParseText(file2).Dump();

    var file3 = "a,\"b\",c\r\nx,\"y\r\nz\"";
    CSV.ParseText(file3).Dump();

    var file4 = "\"\"\"\"";
    CSV.ParseText(file4).Dump();
}

static class CSV
{
    public struct Record
    {
        public readonly string[] Row;

        public string this[int index] => Row[index];

        public Record(string[] row)
        {
            Row = row;
        }
    }

    public static List<Record> ParseText(string text)
    {
        return Parse(new StringReader(text));
    }

    public static List<Record> ParseFile(string fn)
    {
        using (var reader = File.OpenText(fn))
        {
            return Parse(reader);
        }
    }

    public static List<Record> Parse(TextReader reader)
    {
        var data = new List<Record>();

        var col = new StringBuilder();
        var row = new List<string>();
        for (; ; )
        {
            var ln = reader.ReadLine();
            if (ln == null) break;
            if (Tokenize(ln, col, row))
            {
                data.Add(new Record(row.ToArray()));
                row.Clear();
            }
        }

        return data;
    }

    public static bool Tokenize(string s, StringBuilder col, List<string> row)
    {
        int i = 0;

        if (col.Length > 0)
        {
            col.AppendLine(); // continuation

            if (!TokenizeQuote(s, ref i, col, row))
            {
                return false;
            }
        }

        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == ',')
            {
                row.Add(col.ToString().Trim());
                col.Length = 0;
                i++;
            }
            else if (ch == '"')
            {
                i++;
                if (!TokenizeQuote(s, ref i, col, row))
                {
                    return false;
                }
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }

        if (col.Length > 0)
        {
            row.Add(col.ToString().Trim());
            col.Length = 0;
        }

        return true;
    }

    public static bool TokenizeQuote(string s, ref int i, StringBuilder col, List<string> row)
    {
        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == '"')
            {
                // escape sequence
                if (i + 1 < s.Length && s[i + 1] == '"')
                {
                    col.Append('"');
                    i++;
                    i++;
                    continue;
                }
                i++;
                return true;
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }
        return false;
    }
}
John Leidegren
quelle
1

Eine weitere in dieser Liste ist Cinchoo ETL - eine Open-Source-Bibliothek zum Lesen und Schreiben mehrerer Dateiformate (CSV , Flatfile , XML , JSON usw.).

Das folgende Beispiel zeigt, wie CSV-Dateien schnell gelesen werden können (kein POCO-Objekt erforderlich).

string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

using (var p = ChoCSVReader.LoadText(csv)
    .WithFirstLineHeader()
    )
{
    foreach (var rec in p)
    {
        Console.WriteLine($"Id: {rec.Id}");
        Console.WriteLine($"Name: {rec.Name}");
    }
}

Das folgende Beispiel zeigt, wie eine CSV-Datei mit einem POCO-Objekt gelesen wird

public partial class EmployeeRec
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void CSVTest()
{
    string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

    using (var p = ChoCSVReader<EmployeeRec>.LoadText(csv)
        .WithFirstLineHeader()
        )
    {
        foreach (var rec in p)
        {
            Console.WriteLine($"Id: {rec.Id}");
            Console.WriteLine($"Name: {rec.Name}");
        }
    }
}

Weitere Informationen zur Verwendung finden Sie in den Artikeln bei CodeProject .

RajN
quelle
0

Basierend auf dem Beitrag von unlimit zum Thema Wie kann eine CSV mit der Funktion C # split () richtig aufgeteilt werden? ::

string[] tokens = System.Text.RegularExpressions.Regex.Split(paramString, ",");

HINWEIS: Dies behandelt keine maskierten / verschachtelten Kommas usw. und ist daher nur für bestimmte einfache CSV-Listen geeignet.

radsdau
quelle
2
Das ist sehr schlecht und wahrscheinlich langsam :)
EKS
1
Wahrscheinlich, aber es funktioniert perfekt und einfach für einen kleinen Satz von Parametern, ist daher eine gültige und hilfreiche Lösung. Warum abstimmen? "Very Bad" ist ein bisschen extrem, findest du nicht?
Radsdau
1
Es behandelt keine
maskierten
Du hast recht; Ich werde die Antwort bearbeiten, um dies widerzuspiegeln. Vielen Dank. Aber es hat immer noch seinen Platz.
Radsdau
Dies funktionierte perfekt für meinen Anwendungsfall, in dem ich eine SQL Server-CLR-DLL erstelle und keines dieser anderen externen Pakete verwenden kann. Ich musste nur eine einfache CSV-Datei mit einem Dateinamen und einer Zeilenanzahl analysieren.
dubvfan87
0

Dieser Code liest csv in DataTable:

public static DataTable ReadCsv(string path)
{
    DataTable result = new DataTable("SomeData");
    using (TextFieldParser parser = new TextFieldParser(path))
    {
        parser.TextFieldType = FieldType.Delimited;
        parser.SetDelimiters(",");
        bool isFirstRow = true;
        //IList<string> headers = new List<string>();

        while (!parser.EndOfData)
        {
            string[] fields = parser.ReadFields();
            if (isFirstRow)
            {
                foreach (string field in fields)
                {
                    result.Columns.Add(new DataColumn(field, typeof(string)));
                }
                isFirstRow = false;
            }
            else
            {
                int i = 0;
                DataRow row = result.NewRow();
                foreach (string field in fields)
                {
                    row[i++] = field;
                }
                result.Rows.Add(row);
            }
        }
    }
    return result;
}
Polina-c
quelle
1
TextFieldParser befindet sich in Microsoft.VisualBasic.dll.
user3285954
0

Wenn jemand ein Snippet möchte, kann er sich in seinen Code einfügen, ohne eine Bibliothek binden oder ein Paket herunterladen zu müssen. Hier ist eine Version, die ich geschrieben habe:

    public static string FormatCSV(List<string> parts)
    {
        string result = "";

        foreach (string s in parts)
        {
            if (result.Length > 0)
            {
                result += ",";

                if (s.Length == 0)
                    continue;
            }

            if (s.Length > 0)
            {
                result += "\"" + s.Replace("\"", "\"\"") + "\"";
            }
            else
            {
                // cannot output double quotes since its considered an escape for a quote
                result += ",";
            }
        }

        return result;
    }

    enum CSVMode
    {
        CLOSED = 0,
        OPENED_RAW = 1,
        OPENED_QUOTE = 2
    }

    public static List<string> ParseCSV(string input)
    {
        List<string> results;

        CSVMode mode;

        char[] letters;

        string content;


        mode = CSVMode.CLOSED;

        content = "";
        results = new List<string>();
        letters = input.ToCharArray();

        for (int i = 0; i < letters.Length; i++)
        {
            char letter = letters[i];
            char nextLetter = '\0';

            if (i < letters.Length - 1)
                nextLetter = letters[i + 1];

            // If its a quote character
            if (letter == '"')
            {
                // If that next letter is a quote
                if (nextLetter == '"' && mode == CSVMode.OPENED_QUOTE)
                {
                    // Then this quote is escaped and should be added to the content

                    content += letter;

                    // Skip the escape character
                    i++;
                    continue;
                }
                else
                {
                    // otherwise its not an escaped quote and is an opening or closing one
                    // Character is skipped

                    // If it was open, then close it
                    if (mode == CSVMode.OPENED_QUOTE)
                    {
                        results.Add(content);

                        // reset the content
                        content = "";

                        mode = CSVMode.CLOSED;

                        // If there is a next letter available
                        if (nextLetter != '\0')
                        {
                            // If it is a comma
                            if (nextLetter == ',')
                            {
                                i++;
                                continue;
                            }
                            else
                            {
                                throw new Exception("Expected comma. Found: " + nextLetter);
                            }
                        }
                    }
                    else if (mode == CSVMode.OPENED_RAW)
                    {
                        // If it was opened raw, then just add the quote 
                        content += letter;
                    }
                    else if (mode == CSVMode.CLOSED)
                    {
                        // Otherwise open it as a quote 

                        mode = CSVMode.OPENED_QUOTE;
                    }
                }
            }
            // If its a comma seperator
            else if (letter == ',')
            {
                // If in quote mode
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    // Just read it
                    content += letter;
                }
                // If raw, then close the content
                else if (mode == CSVMode.OPENED_RAW)
                {
                    results.Add(content);

                    content = "";

                    mode = CSVMode.CLOSED;
                }
                // If it was closed, then open it raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    results.Add(content);

                    content = "";
                }
            }
            else
            {
                // If opened quote, just read it
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    content += letter;
                }
                // If opened raw, then read it
                else if (mode == CSVMode.OPENED_RAW)
                {
                    content += letter;
                }
                // It closed, then open raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    content += letter;
                }
            }
        }

        // If it was still reading when the buffer finished
        if (mode != CSVMode.CLOSED)
        {
            results.Add(content);
        }

        return results;
    }
John
quelle
0

Hier ist eine kurze und einfache Lösung.

                using (TextFieldParser parser = new TextFieldParser(outputLocation))
                 {
                        parser.TextFieldType = FieldType.Delimited;
                        parser.SetDelimiters(",");
                        string[] headers = parser.ReadLine().Split(',');
                        foreach (string header in headers)
                        {
                            dataTable.Columns.Add(header);
                        }
                        while (!parser.EndOfData)
                        {
                            string[] fields = parser.ReadFields();
                            dataTable.Rows.Add(fields);
                        }
                    }
Der Falke
quelle