it-swarm.com.de

Analyse von CSV-Dateien in C # mit Header

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

Ich habe auch Fälle von Benutzern gesehen, die ODBC/OLE DB zum Lesen von CSV über den Texttreiber verwenden, und viele Leute entmutigen dies aufgrund ihrer "Nachteile". Was sind diese Nachteile?

Idealerweise suche ich nach einem Weg, auf dem ich die CSV nach Spaltennamen lesen kann, wobei der erste Datensatz als Kopf-/Feldnamen verwendet wird. Einige der Antworten sind korrekt, dienen jedoch dazu, die Datei grundsätzlich in Klassen zu deserialisieren.

220
David Pfeffer

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

Check out FileHelpers und bleib DRY - Wiederhole dich nicht - du musst das Rad nicht mehr als eine Million mal neu erfinden ...

Sie müssen diese Form Ihrer Daten - die Felder in Ihrer individuellen Zeile in der CSV - im Allgemeinen nur mithilfe einer öffentlichen Klasse (und damit durchdachten Attributen wie Standardwerten, Ersetzung von NULL-Werten usw.) definieren die FileHelpers-Engine in einer Datei und Bingo - Sie erhalten alle Einträge aus dieser Datei zurück. Eine einfache Bedienung - tolle Leistung!

121
marc_s

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

Fügen Sie einen Verweis auf Microsoft.VisualBasic.dll hinzu (funktioniert in C # einwandfrei, machen Sie sich nichts aus dem Namen)

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

288
Alex

CsvHelper (eine von mir gepflegte 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, die Sie lesen möchten. In diesem Fall können Sie eine fließende Zuordnung verwenden, da Sie der Klasse keine Attribute hinzufügen 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>();
    }
}
162
Josh Close

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 auf dem Link, den ich zur Verfügung gestellt 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, ist es sehr einfach, damit zu arbeiten.

30
alexn

Ich weiß, es ist etwas spät, aber ich habe gerade eine Bibliothek Microsoft.VisualBasic.FileIO gefunden, die TextFieldParser-Klasse hat, um csv-Dateien zu verarbeiten.

17
user1131926

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

Beide sind frei und Open Source.

12
Giorgi

Hier ist eine Helfer-Klasse, die ich häufig verwende, falls jemand jemals zu diesem Thread zurückkommt (ich wollte ihn teilen).

Ich benutze dies für die einfache Portierung in gebrauchsfertige Projekte:

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 erstellte]

10
Base33

Diese Lösung verwendet die offizielle Microsoft.VisualBasic Assembly, um CSV zu analysieren.

Vorteile:

  • Trennzeichen für Flucht
  • ignoriert Header 
  • räume zuschneiden
  • 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;
    }
8
Jonas_Hess

Ich habe TinyCsvParser für .NET geschrieben, das zu den schnellsten .NET-Parsern gehört und in hohem Maße konfigurierbar ist, um fast jedes CSV-Format zu parsen.

Es ist unter der Lizenz MIT veröffentlicht:

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

PM> Install-Package TinyCsvParser

Verwendungszweck

Stellen Sie sich vor, wir haben eine Liste von Personen in einer CSV-Datei persons.csv mit 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 so 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 die Zuordnung verwenden, um die CSV-Daten mit einer CsvParser zu analysieren.

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

Eine vollständige Bedienungsanleitung finden Sie unter:

7
bytefish

Es gibt keinen offiziellen Weg, von dem ich weiß, aber Sie sollten tatsächlich vorhandene Bibliotheken verwenden. Hier ist eine, die ich in CodeProject wirklich nützlich fand:

http://www.codeproject.com/KB/database/CsvReader.aspx

3
VitalyB

Dateilösung mit einer Quelle für unkomplizierte Analyseanforderungen, nützlich. Befasst sich mit allen bösen Edge-Fällen. Wie die Normalisierung neuer Zeilen und das Handling neuer Zeilen in zitierten String-Literalen. Herzlich Willkommen

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

Beachten Sie, dass Dump eine LINQPad-Methode ist, die Sie möglicherweise entfernen möchten, 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;
    }
}
1
John Leidegren

Vor einiger Zeit hatte ich eine einfache Klasse für CSV-Lese-/Schreibvorgänge basierend auf Microsoft.VisualBasic-Bibliothek 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 der Kopfzeile müssen Sie nur csv.Rows[0] Zellen lesen :)

1
Andrew

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;
    }

}
1
Alex Begun

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

Das folgende Beispiel zeigt, wie die CSV-Datei schnell gelesen werden kann (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 CSV-Dateien mit POCO-Objekten gelesen werden

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}");
        }
    }
}

Bitte lesen Sie die Artikel unter CodeProject zur Verwendung.

0
RajN

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;
}
0
PolinaC

Basierend auf dem Beitrag von unlimit auf Wie teilt man eine CSV richtig mit der C # split () - Funktion auf? :

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

ANMERKUNG: Dies behandelt keine mit Escapezeichen versehenen/geschachtelten Kommas usw. und ist daher nur für bestimmte einfache CSV-Listen geeignet.

0
radsdau