So lesen Sie eine CSV-Datei in eine .NET-Datentabelle

170

Wie kann ich eine CSV-Datei in eine laden System.Data.DataTableund die Datentabelle basierend auf der CSV-Datei erstellen?

Ermöglicht die reguläre ADO.net-Funktionalität dies?

Ronnie Overby
quelle
21
Wie ist das möglicherweise "off topic"? Es ist eine spezielle Frage und 100 Leute finden sie nützlich
Ryan
10
@ Ryan: Wahrlich, ich sage dir ... StackOverflow-Moderatoren sind eine Brut von Vipern. Geh hinter mich, StackOverflow-Moderatoren!
Ronnie Overby

Antworten:

89

Hier ist eine hervorragende Klasse, die CSV-Daten mithilfe der Datenstruktur zum Erstellen der Datentabelle in eine Datentabelle kopiert:

Ein tragbarer und effizienter generischer Parser für Flatfiles

Es ist einfach zu konfigurieren und einfach zu bedienen. Ich fordere Sie auf, einen Blick darauf zu werfen.

Jay Riggs
quelle
In der Tat ausgezeichnet. Es funktionierte sofort perfekt für mich, ohne die Dokumentation zu lesen.
Grinsender
Funktioniert dies bei CSV-Dateien, bei denen jede Zeile eine andere Struktur haben kann? Ich habe eine Protokolldatei mit verschiedenen Arten von protokollierten Ereignissen, die in mehrere Tabellen aufgeteilt werden müssten.
Gonzobrains
2
@gonzobrains - Wahrscheinlich nicht; Die Grundannahme einer CSV-Datei ist eine rechteckige Datenstruktur, die auf einem einzelnen Satz von Spaltenüberschriften basiert, die in der ersten Zeile angegeben sind. Was Sie haben, scheinen allgemeinere, durch Kommas getrennte, diskriminierte Daten zu sein, die eine komplexere "ETL" erfordern, um aus der Datei in Objektinstanzen unterschiedlichen Typs zu analysieren (einschließlich DataRows verschiedener DataTables).
KeithS
93

Ich habe OleDbAnbieter verwendet. Es gibt jedoch Probleme, wenn Sie Zeilen mit numerischen Werten lesen, diese jedoch als Text behandeln möchten. Sie können dieses Problem jedoch umgehen, indem Sie eine schema.iniDatei erstellen. Hier ist meine Methode, die ich verwendet habe:

// using System.Data;
// using System.Data.OleDb;
// using System.Globalization;
// using System.IO;

static DataTable GetDataTableFromCsv(string path, bool isFirstRowHeader)
{
    string header = isFirstRowHeader ? "Yes" : "No";

    string pathOnly = Path.GetDirectoryName(path);
    string fileName = Path.GetFileName(path);

    string sql = @"SELECT * FROM [" + fileName + "]";

    using(OleDbConnection connection = new OleDbConnection(
              @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathOnly + 
              ";Extended Properties=\"Text;HDR=" + header + "\""))
    using(OleDbCommand command = new OleDbCommand(sql, connection))
    using(OleDbDataAdapter adapter = new OleDbDataAdapter(command))
    {
        DataTable dataTable = new DataTable();
        dataTable.Locale = CultureInfo.CurrentCulture;
        adapter.Fill(dataTable);
        return dataTable;
    }
}
Jim Scott
quelle
Danke Kumpel. Das hat mir geholfen. Ich hatte eine CSV-Datei, in der Kommas nicht nur Trennzeichen waren, sondern sich überall in vielen Spaltenwerten befanden. Daher war es eine Herausforderung, einen regulären Ausdruck zu finden, der die Zeile aufteilt. Der OleDbProvider hat das Schema korrekt abgeleitet.
Galilyou
Die Implementierung ist sinnvoll, aber wie gehen wir mit Zellen um, die gemischte Datentypen enthalten? Zum Beispiel 40C und etc.?
GKED
GKED: Wenn die Daten, die Sie einlesen, immer einen erwarteten Satz von Spalten und Typen enthalten, können Sie im selben Ordner eine shema.ini-Datei ablegen, die Informationen zum OleDb-Anbieter über die Spalten enthält. Hier ist ein Link zu einem Microsoft-Artikel, der Details zur Strukturierung der Datei enthält. msdn.microsoft.com/en-us/library/…
Jim Scott
4
Obwohl diese Antwort funktionieren wird, würde ich dringend davon abraten. Sie führen eine externe Abhängigkeit ein, die abhängig von den installierten Versionen zu Konflikten mit anderen Office-Installationen auf demselben Computer führen kann (Excel in Ihrer lokalen Entwicklungsumgebung verwenden?). Es gibt NuGet-Pakete (ExcelDataReader, CsvHelper), die dies effizienter und portabler tun.
A. Murray
1
@ A. Murray - Was genau meinst du? Dies verwendet den in System.Data.dll integrierten OleDb-Anbieter. Sie müssen keine zusätzlichen "Treiber" installieren. Und ich wäre heutzutage schockiert, wenn bei einer Windows-Installation nicht der grundlegende Jet-Treiber installiert wäre. Dies ist 1990er CSV ....
Paul Easter
40

Ich habe mich entschieden zu verwenden Sebastien Lorions Csv Reader entschieden .

Jay Riggs Vorschlag ist auch eine großartige Lösung, aber ich brauchte einfach nicht alle Funktionen, die das bietet Andrew Rissings Generic Parser bietet.

UPDATE 25.10.2010

Nach dem Gebrauch ich fast anderthalb Jahre lang den CSV-Reader Sebastien Lorion in meinem Projekt verwendet habe, habe ich festgestellt, dass er beim Parsen einiger CSV-Dateien, von denen ich glaube, dass sie gut geformt sind, Ausnahmen auslöst.

Also habe ich zu Andrew Rissings Generic Parser gewechselt und es scheint viel besser zu laufen.

UPDATE 22.09.2014

Heutzutage verwende ich meistens diese Erweiterungsmethode, um abgegrenzten Text zu lesen:

https://github.com/Core-Techs/Common/blob/master/CoreTechs.Common/Text/DelimitedTextExtensions.cs#L22

https://www.nuget.org/packages/CoreTechs.Common/

UPDATE 20.02.2015

Beispiel:

var csv = @"Name, Age
Ronnie, 30
Mark, 40
Ace, 50";

TextReader reader = new StringReader(csv);
var table = new DataTable();
using(var it = reader.ReadCsvWithHeader().GetEnumerator())
{

    if (!it.MoveNext()) return;

    foreach (var k in it.Current.Keys)
        table.Columns.Add(k);

    do
    {
        var row = table.NewRow();
        foreach (var k in it.Current.Keys)
            row[k] = it.Current[k];
    
        table.Rows.Add(row);
    
    } while (it.MoveNext());
}
Ronnie Overby
quelle
Ich stimme zu, dass Sebastien Loriens CSV-Leser großartig ist. Ich benutze es für schwere CSV-Verarbeitung, aber ich habe Andrews Rissing's auch für kleine Jobs verwendet und es hat mir gute Dienste geleistet. Habe Spaß!
Jay Riggs
Wie kann ich diese Klassen verwenden, um CSV in DATATABLE zu laden?
Muflix
Ich habe dies versucht, aber die it.Current.Keys-Auflistung wird mit "System.Linq.Enumerable + WhereSelectListIterator`2 [System.Int32, System.Char]" anstelle des Namens der Spalte zurückgegeben. Irgendwelche Gedanken darüber warum?
user3658298
Können Sie Trennzeichen für mehrere Zeichen verwenden?
rollt
Nein, aber ich habe darüber nachgedacht, dies zu ermöglichen.
Ronnie Overby
32

Hey, es funktioniert zu 100%

  public static DataTable ConvertCSVtoDataTable(string strFilePath)
  {
    DataTable dt = new DataTable();
    using (StreamReader sr = new StreamReader(strFilePath))
    {
        string[] headers = sr.ReadLine().Split(',');
        foreach (string header in headers)
        {
            dt.Columns.Add(header);
        }
        while (!sr.EndOfStream)
        {
            string[] rows = sr.ReadLine().Split(',');
            DataRow dr = dt.NewRow();
            for (int i = 0; i < headers.Length; i++)
            {
                dr[i] = rows[i];
            }
            dt.Rows.Add(dr);
        }

    }


    return dt;
   }

CSV-Bild Geben Sie hier die Bildbeschreibung ein

Datentabelle Importiert Geben Sie hier die Bildbeschreibung ein

Shivam Srivastava
quelle
7
Nur wenn 100% der Eingaben die einfachste CSV-Datei sind (was in Ihrem Fall der Fall sein kann).
Ronnie Overby
Du hast Recht. Sie sollten codeproject.com/Articles/9258/A-Fast-CSV-Reader (Lorion dll) verwenden. Ich habe versucht, dass es gut funktioniert.
Shivam Srivastava
1
Siehe meine Antwort von 2009.
Ronnie Overby
1
@ ShivamSrivastava Ich bekomme den Fehler in der letzten Zeile Sind Sie da, dann geben Sie andere Kontaktinformationen
Sunil Acharya
Obwohl ich diese Version nicht genau verwendet habe, habe ich daraufhin mein Problem gelöst. Danke dir. Funktioniert sehr gut.
Nrod
13

Wir haben immer den Jet.OLEDB-Treiber verwendet, bis wir zu 64-Bit-Anwendungen gingen. Microsoft hat und wird keinen 64-Bit-Jet-Treiber veröffentlichen. Hier ist eine einfache Lösung, die File.ReadAllLines und String.Split verwendet, um die CSV-Datei zu lesen und zu analysieren und eine DataTable manuell zu laden. Wie oben erwähnt, wird die Situation, in der einer der Spaltenwerte ein Komma enthält, NICHT behandelt. Wir verwenden dies hauptsächlich zum Lesen benutzerdefinierter Konfigurationsdateien. Das Schöne an der Verwendung von CSV-Dateien ist, dass wir sie in Excel bearbeiten können.

string CSVFilePathName = @"C:\test.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols; i++)
    dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 1; i < Lines.GetLength(0); i++)
{
    Fields = Lines[i].Split(new char[] { ',' });
    Row = dt.NewRow();
    for (int f = 0; f < Cols; f++)
        Row[f] = Fields[f];
    dt.Rows.Add(Row);
}
Chuck Bevitt
quelle
8

Dies ist der Code, den ich benutze, aber Ihre Apps müssen mit Net Version 3.5 ausgeführt werden

private void txtRead_Click(object sender, EventArgs e)
        {
           // var filename = @"d:\shiptest.txt";

            openFileDialog1.InitialDirectory = "d:\\";
            openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            DialogResult result = openFileDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                if (openFileDialog1.FileName != "")
                {
                    var reader = ReadAsLines(openFileDialog1.FileName);

                    var data = new DataTable();

                    //this assume the first record is filled with the column names
                    var headers = reader.First().Split(',');
                    foreach (var header in headers)
                    {
                        data.Columns.Add(header);
                    }

                    var records = reader.Skip(1);
                    foreach (var record in records)
                    {
                        data.Rows.Add(record.Split(','));
                    }

                    dgList.DataSource = data;
                }
            }
        }

        static IEnumerable<string> ReadAsLines(string filename)
        {
            using (StreamReader reader = new StreamReader(filename))
                while (!reader.EndOfStream)
                    yield return reader.ReadLine();
        }
Thomas
quelle
Das ist so ziemlich das, was ich präsentieren wollte.
Kapitän Kenpachi
8

Sie können dies erreichen, indem Sie die DLL Microsoft.VisualBasic.FileIO.TextFieldParser in C # verwenden

static void Main()
        {
            string csv_file_path=@"C:\Users\Administrator\Desktop\test.csv";

            DataTable csvData = GetDataTabletFromCSVFile(csv_file_path);

            Console.WriteLine("Rows count:" + csvData.Rows.Count);

            Console.ReadLine();
        }


private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
        {
            DataTable csvData = new DataTable();

            try
            {

            using(TextFieldParser csvReader = new TextFieldParser(csv_file_path))
                {
                    csvReader.SetDelimiters(new string[] { "," });
                    csvReader.HasFieldsEnclosedInQuotes = true;
                    string[] colFields = csvReader.ReadFields();
                    foreach (string column in colFields)
                    {
                        DataColumn datecolumn = new DataColumn(column);
                        datecolumn.AllowDBNull = true;
                        csvData.Columns.Add(datecolumn);
                    }

                    while (!csvReader.EndOfData)
                    {
                        string[] fieldData = csvReader.ReadFields();
                        //Making empty value as null
                        for (int i = 0; i < fieldData.Length; i++)
                        {
                            if (fieldData[i] == "")
                            {
                                fieldData[i] = null;
                            }
                        }
                        csvData.Rows.Add(fieldData);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            return csvData;
        }
kombsh
quelle
Bitte versuchen Sie nicht, das Rad mit CSV-Verarbeitung neu zu erfinden. Es gibt so viele großartige Open Source-Alternativen, die sehr robust sind.
Mike Cole
1
Vielen Dank an Brad, nützlicher Tipp in Bezug auf TextFieldParser für den Umgang mit eingebetteten Anführungszeichen.
Mattpm
3
public class Csv
{
    public static DataTable DataSetGet(string filename, string separatorChar, out List<string> errors)
    {
        errors = new List<string>();
        var table = new DataTable("StringLocalization");
        using (var sr = new StreamReader(filename, Encoding.Default))
        {
            string line;
            var i = 0;
            while (sr.Peek() >= 0)
            {
                try
                {
                    line = sr.ReadLine();
                    if (string.IsNullOrEmpty(line)) continue;
                    var values = line.Split(new[] {separatorChar}, StringSplitOptions.None);
                    var row = table.NewRow();
                    for (var colNum = 0; colNum < values.Length; colNum++)
                    {
                        var value = values[colNum];
                        if (i == 0)
                        {
                            table.Columns.Add(value, typeof (String));
                        }
                        else
                        {
                            row[table.Columns[colNum]] = value;
                        }
                    }
                    if (i != 0) table.Rows.Add(row);
                }
                catch(Exception ex)
                {
                    errors.Add(ex.Message);
                }
                i++;
            }
        }
        return table;
    }
}
Nodir
quelle
3

Ich bin auf diesen Code gestoßen, der Linq und Regex verwendet, um eine CSV-Datei zu analysieren. Der Artikel, auf den verwiesen wird, ist jetzt über anderthalb Jahre alt, hat jedoch keine bessere Möglichkeit gefunden, eine CSV mit Linq (und Regex) zu analysieren. Die Einschränkung ist, dass der hier angewendete reguläre Ausdruck für durch Kommas getrennte Dateien gilt (erkennt Kommas in Anführungszeichen!) Und dass es für Header möglicherweise nicht gut ist, aber es gibt eine Möglichkeit, diese zu überwinden). Nehmen Sie einen Höhepunkt:

Dim lines As String() = System.IO.File.ReadAllLines(strCustomerFile)
Dim pattern As String = ",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"
Dim r As System.Text.RegularExpressions.Regex = New System.Text.RegularExpressions.Regex(pattern)
Dim custs = From line In lines _
            Let data = r.Split(line) _
                Select New With {.custnmbr = data(0), _
                                 .custname = data(1)}
For Each cust In custs
    strCUSTNMBR = Replace(cust.custnmbr, Chr(34), "")
    strCUSTNAME = Replace(cust.custname, Chr(34), "")
Next
Nepa
quelle
3

Die beste Option, die ich gefunden habe und die Probleme behebt, bei denen möglicherweise verschiedene Versionen von Office installiert sind, sowie 32/64-Bit-Probleme, wie sie von Chuck Bevitt erwähnt wurden , sind FileHelpers .

Es kann mit NuGet zu Ihren Projektreferenzen hinzugefügt werden und bietet eine einzeilige Lösung:

CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);
Neo
quelle
Kannst du sagen, was CommonEngine ist? Ist NuGet dasselbe mit NuGet.Core? Ich fand nur NuGet.Core in Referenzen
Sindhu Jampani
Es sind FileHelpers, die Sie brauchen. Wenn Sie NuGet haben, fügen Sie es mit NuGet hinzu. Andernfalls fügen Sie es einfach als Baugruppe in Ihr Projekt ein. CommonEngine ist Teil von FileHelpers.
Neo
3

Für diejenigen unter Ihnen, die keine externe Bibliothek verwenden möchten und OleDB nicht verwenden möchten, siehe das folgende Beispiel. Alles, was ich gefunden habe, war entweder OleDB, eine externe Bibliothek oder einfach das Aufteilen anhand eines Kommas! Für meinen Fall funktionierte OleDB nicht, also wollte ich etwas anderes.

Ich habe einen Artikel von MarkJ gefunden, der auf die hier gezeigte Microsoft.VisualBasic.FileIO.TextFieldParser-Methode verweist . Der Artikel ist in VB geschrieben und gibt keine Datentabelle zurück. Siehe mein Beispiel unten.

public static DataTable LoadCSV(string path, bool hasHeader)
    {
        DataTable dt = new DataTable();

        using (var MyReader = new Microsoft.VisualBasic.FileIO.TextFieldParser(path))
        {
            MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
            MyReader.Delimiters = new String[] { "," };

            string[] currentRow;

            //'Loop through all of the fields in the file.  
            //'If any lines are corrupt, report an error and continue parsing.  
            bool firstRow = true;
            while (!MyReader.EndOfData)
            {
                try
                {
                    currentRow = MyReader.ReadFields();

                    //Add the header columns
                    if (hasHeader && firstRow)
                    {
                        foreach (string c in currentRow)
                        {
                            dt.Columns.Add(c, typeof(string));
                        }

                        firstRow = false;
                        continue;
                    }

                    //Create a new row
                    DataRow dr = dt.NewRow();
                    dt.Rows.Add(dr);

                    //Loop thru the current line and fill the data out
                    for(int c = 0; c < currentRow.Count(); c++)
                    {
                        dr[c] = currentRow[c];
                    }
                }
                catch (Microsoft.VisualBasic.FileIO.MalformedLineException ex)
                {
                    //Handle the exception here
                }
            }
        }

        return dt;
    }
Smeiff
quelle
3

Sehr einfache Antwort: Wenn Sie keine komplexe CSV-Datei haben, die eine einfache Split-Funktion verwenden kann, funktioniert dies gut für den Import (beachten Sie, dass dies als Zeichenfolgen importiert wird, ich mache Datentypkonvertierungen später, wenn ich muss).

 private DataTable csvToDataTable(string fileName, char splitCharacter)
    {                
        StreamReader sr = new StreamReader(fileName);
        string myStringRow = sr.ReadLine();
        var rows = myStringRow.Split(splitCharacter);
        DataTable CsvData = new DataTable();
        foreach (string column in rows)
        {
            //creates the columns of new datatable based on first row of csv
            CsvData.Columns.Add(column);
        }
        myStringRow = sr.ReadLine();
        while (myStringRow != null)
        {
            //runs until string reader returns null and adds rows to dt 
            rows = myStringRow.Split(splitCharacter);
            CsvData.Rows.Add(rows);
            myStringRow = sr.ReadLine();
        }
        sr.Close();
        sr.Dispose();
        return CsvData;
    }

Meine Methode, wenn ich eine Tabelle mit einem String [] -Trennzeichen importiere und das Problem behandle, bei dem die aktuelle Zeile, die ich lese, möglicherweise zur nächsten Zeile in der CSV- oder Textdatei <- gegangen ist. In diesem Fall möchte ich eine Schleife ausführen, bis ich sie erhalte auf die Gesamtzahl der Zeilen in der ersten Zeile (Spalten)

public static DataTable ImportCSV(string fullPath, string[] sepString)
    {
        DataTable dt = new DataTable();
        using (StreamReader sr = new StreamReader(fullPath))
        {
           //stream uses using statement because it implements iDisposable
            string firstLine = sr.ReadLine();
            var headers = firstLine.Split(sepString, StringSplitOptions.None);
            foreach (var header in headers)
            {
               //create column headers
                dt.Columns.Add(header);
            }
            int columnInterval = headers.Count();
            string newLine = sr.ReadLine();
            while (newLine != null)
            {
                //loop adds each row to the datatable
                var fields = newLine.Split(sepString, StringSplitOptions.None); // csv delimiter    
                var currentLength = fields.Count();
                if (currentLength < columnInterval)
                {
                    while (currentLength < columnInterval)
                    {
                       //if the count of items in the row is less than the column row go to next line until count matches column number total
                        newLine += sr.ReadLine();
                        currentLength = newLine.Split(sepString, StringSplitOptions.None).Count();
                    }
                    fields = newLine.Split(sepString, StringSplitOptions.None);
                }
                if (currentLength > columnInterval)
                {  
                    //ideally never executes - but if csv row has too many separators, line is skipped
                    newLine = sr.ReadLine();
                    continue;
                }
                dt.Rows.Add(fields);
                newLine = sr.ReadLine();
            }
            sr.Close();
        }

        return dt;
    }
Matt Farguson
quelle
Schön, dass Sie Zeilen noch nicht als Zeichenfolge [] deklariert haben.
Animal Style
@ TierStyle Sie haben Recht - aktualisiert mit robusterer Methode und deklarierten Zeilen
Matt Farguson
3

Geändert von Herrn ChuckBevitt

Arbeitslösung:

string CSVFilePathName = APP_PATH + "Facilities.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols-1; i++)
        dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 0; i < Lines.GetLength(0)-1; i++)
{
        Fields = Lines[i].Split(new char[] { ',' });
        Row = dt.NewRow();
        for (int f = 0; f < Cols-1; f++)
                Row[f] = Fields[f];
        dt.Rows.Add(Row);
}
Balaji Selvarajan
quelle
Das löst also ein Speicherproblem, oder? Dies ist eine zeilenweise Verarbeitung, die nicht im Speicher verbleibt, sodass es keine Ausnahmen geben sollte. Ich mag die Art und Weise, wie dies verarbeitet wird, aber speichert File.ReadAllLines () nicht alles im Speicher? Ich denke, Sie sollten File.ReadLines () verwenden, um großen Speicherpuffer zu vermeiden? Dies ist eine gute Antwort auf die vorliegende Frage, die ich nur über Speicherprobleme wissen möchte.
DtechNet
2

Hier ist eine Lösung, die den ODBC-Texttreiber von ADO.Net verwendet:

Dim csvFileFolder As String = "C:\YourFileFolder"
Dim csvFileName As String = "YourFile.csv"

'Note that the folder is specified in the connection string,
'not the file. That's specified in the SELECT query, later.
Dim connString As String = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" _
    & csvFileFolder & ";Extended Properties=""Text;HDR=No;FMT=Delimited"""
Dim conn As New Odbc.OdbcConnection(connString)

'Open a data adapter, specifying the file name to load
Dim da As New Odbc.OdbcDataAdapter("SELECT * FROM [" & csvFileName & "]", conn)
'Then fill a data table, which can be bound to a grid
Dim dt As New DataTableda.Fill(dt)

grdCSVData.DataSource = dt

Nach dem Ausfüllen können Sie Eigenschaften der Datentabelle wie ColumnName bewerten, um alle Funktionen der ADO.Net-Datenobjekte zu nutzen.

In VS2008 können Sie Linq verwenden, um den gleichen Effekt zu erzielen.

HINWEIS: Dies kann ein Duplikat dieser SO-Frage sein.

Bob Mc
quelle
2

Ich kann nicht widerstehen, meinen eigenen Dreh hinzuzufügen. Das ist so viel besser und kompakter als das, was ich in der Vergangenheit verwendet habe.

Diese Lösung:

  • Hängt nicht von einem Datenbanktreiber oder einer Bibliothek eines Drittanbieters ab.
  • Schlägt nicht bei doppelten Spaltennamen fehl
  • Behandelt Kommas in den Daten
  • Behandelt alle Trennzeichen, nicht nur Kommas (obwohl dies die Standardeinstellung ist).

Folgendes habe ich mir ausgedacht:

  Public Function ToDataTable(FileName As String, Optional Delimiter As String = ",") As DataTable
    ToDataTable = New DataTable
    Using TextFieldParser As New Microsoft.VisualBasic.FileIO.TextFieldParser(FileName) With
      {.HasFieldsEnclosedInQuotes = True, .TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited, .TrimWhiteSpace = True}
      With TextFieldParser
        .SetDelimiters({Delimiter})
        .ReadFields.ToList.Unique.ForEach(Sub(x) ToDataTable.Columns.Add(x))
        ToDataTable.Columns.Cast(Of DataColumn).ToList.ForEach(Sub(x) x.AllowDBNull = True)
        Do Until .EndOfData
          ToDataTable.Rows.Add(.ReadFields.Select(Function(x) Text.BlankToNothing(x)).ToArray)
        Loop
      End With
    End Using
  End Function

Es hängt von einer Erweiterungsmethode ( Unique) ab, doppelte Spaltennamen zu verarbeiten, die als meine Antwort in gefunden werden unter Anhängen eindeutiger Zahlen an eine Liste von Zeichenfolgen zu finden sind

Und hier ist die BlankToNothingHilfsfunktion:

  Public Function BlankToNothing(ByVal Value As String) As Object 
    If String.IsNullOrEmpty(Value) Then Return Nothing
    Return Value
  End Function
toddmo
quelle
2

Mit Cinchoo ETL - einer Open Source-Bibliothek - können Sie CSV-Dateien mit wenigen Codezeilen problemlos in DataTable konvertieren.

using (var p = new ChoCSVReader(** YOUR CSV FILE **)
     .WithFirstLineHeader()
    )
{
    var dt = p.AsDataTable();
}

Weitere Informationen finden Sie im Artikel zum Codeprojekt .

Ich hoffe es hilft.

RajN
quelle
2
    private static DataTable LoadCsvData(string refPath)
    {
        var cfg = new Configuration() { Delimiter = ",", HasHeaderRecord = true };
        var result = new DataTable();
        using (var sr = new StreamReader(refPath, Encoding.UTF8, false, 16384 * 2))
        {
            using (var rdr = new CsvReader(sr, cfg))
            using (var dataRdr = new CsvDataReader(rdr))
            {
                result.Load(dataRdr);
            }
        }
        return result;
    }

Verwenden von: https://joshclose.github.io/CsvHelper/

Youssef Bouha
quelle
Beachten Sie, dass in Version 13 Configuration umbenannt wurde CsvConfiguration , um Namespace-Konflikte zu vermeiden. Demo dieser Antwort funktioniert: dotnetfiddle.net/sdwc6i
dbc
2

Ich benutze eine Bibliothek namens ExcelDataReader, die Sie auf NuGet finden können. Stellen Sie sicher, dass Sie sowohl ExcelDataReader als auch die Erweiterung ExcelDataReader.DataSet installieren (letztere bietet die erforderliche AsDataSet-Methode, auf die unten verwiesen wird).

Ich habe alles in einer Funktion zusammengefasst, Sie können es direkt in Ihren Code kopieren. Geben Sie einen Pfad zur CSV-Datei ein. Sie erhalten einen Datensatz mit einer Tabelle.

public static DataSet GetDataSet(string filepath)
{
   var stream = File.OpenRead(filepath);

   try
   {
       var reader = ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration()
       {
           LeaveOpen = false
       });

       var result = reader.AsDataSet(new ExcelDataSetConfiguration()
       {
           // Gets or sets a value indicating whether to set the DataColumn.DataType 
           // property in a second pass.
           UseColumnDataType = true,

           // Gets or sets a callback to determine whether to include the current sheet
           // in the DataSet. Called once per sheet before ConfigureDataTable.
           FilterSheet = (tableReader, sheetIndex) => true,

           // Gets or sets a callback to obtain configuration options for a DataTable. 
           ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
           {
               // Gets or sets a value indicating the prefix of generated column names.
               EmptyColumnNamePrefix = "Column",

               // Gets or sets a value indicating whether to use a row from the 
               // data as column names.
               UseHeaderRow = true,

               // Gets or sets a callback to determine which row is the header row. 
               // Only called when UseHeaderRow = true.
               ReadHeaderRow = (rowReader) =>
               {
                   // F.ex skip the first row and use the 2nd row as column headers:
                   //rowReader.Read();
               },

               // Gets or sets a callback to determine whether to include the 
               // current row in the DataTable.
               FilterRow = (rowReader) =>
               {
                   return true;
               },

               // Gets or sets a callback to determine whether to include the specific
               // column in the DataTable. Called once per column after reading the 
               // headers.
               FilterColumn = (rowReader, columnIndex) =>
               {
                   return true;
               }
           }
       });

       return result;
   }
   catch (Exception ex)
   {
       return null;
   }
   finally
   {
       stream.Close();
       stream.Dispose();
   }
}
Dotnetsqlcoder
quelle
Es ist 2020 und dies ist eine großartige Lösung im Vergleich zu einigen der älteren Antworten hier. Es ist schön verpackt und verwendet eine beliebte und leichte Bibliothek von NuGet. Und es ist flexibel - wenn sich Ihre CSV im Speicher befindet, übergeben Sie sie einfach als MemoryStreamDateipfad. Die von OP result.Tables[0]
angeforderte Datentabelle kann
1

Wenn ich nur diese Erweiterungsmethoden teile, hoffe ich, dass sie jemandem helfen können.

public static List<string> ToCSV(this DataSet ds, char separator = '|')
{
    List<string> lResult = new List<string>();

    foreach (DataTable dt in ds.Tables)
    {
        StringBuilder sb = new StringBuilder();
        IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
                                          Select(column => column.ColumnName);
        sb.AppendLine(string.Join(separator.ToString(), columnNames));

        foreach (DataRow row in dt.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field =>
              string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
            sb.AppendLine(string.Join(separator.ToString(), fields));
        }

        lResult.Add(sb.ToString());
    }
    return lResult;
}

public static DataSet CSVtoDataSet(this List<string> collectionCSV, char separator = '|')
{
    var ds = new DataSet();

    foreach (var csv in collectionCSV)
    {
        var dt = new DataTable();

        var readHeader = false;
        foreach (var line in csv.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
        {
            if (!readHeader)
            {
                foreach (var c in line.Split(separator))
                    dt.Columns.Add(c);
            }
            else
            {
                dt.Rows.Add(line.Split(separator));
            }
        }

        ds.Tables.Add(dt);
    }

    return ds;
}
Akaize
quelle
0

Verwenden Sie diese Funktion, um alle Probleme mit Komma und Anführungszeichen zu lösen:

public static DataTable CsvToDataTable(string strFilePath)
    {

        if (File.Exists(strFilePath))
        {

            string[] Lines;
            string CSVFilePathName = strFilePath;

            Lines = File.ReadAllLines(CSVFilePathName);
            while (Lines[0].EndsWith(","))
            {
                Lines[0] = Lines[0].Remove(Lines[0].Length - 1);
            }
            string[] Fields;
            Fields = Lines[0].Split(new char[] { ',' });
            int Cols = Fields.GetLength(0);
            DataTable dt = new DataTable();
            //1st row must be column names; force lower case to ensure matching later on.
            for (int i = 0; i < Cols; i++)
                dt.Columns.Add(Fields[i], typeof(string));
            DataRow Row;
            int rowcount = 0;
            try
            {
                string[] ToBeContinued = new string[]{};
                bool lineToBeContinued = false;
                for (int i = 1; i < Lines.GetLength(0); i++)
                {
                    if (!Lines[i].Equals(""))
                    {
                        Fields = Lines[i].Split(new char[] { ',' });
                        string temp0 = string.Join("", Fields).Replace("\"\"", "");
                        int quaotCount0 = temp0.Count(c => c == '"');
                        if (Fields.GetLength(0) < Cols || lineToBeContinued || quaotCount0 % 2 != 0)
                        {
                            if (ToBeContinued.GetLength(0) > 0)
                            {
                                ToBeContinued[ToBeContinued.Length - 1] += "\n" + Fields[0];
                                Fields = Fields.Skip(1).ToArray();
                            }
                            string[] newArray = new string[ToBeContinued.Length + Fields.Length];
                            Array.Copy(ToBeContinued, newArray, ToBeContinued.Length);
                            Array.Copy(Fields, 0, newArray, ToBeContinued.Length, Fields.Length);
                            ToBeContinued = newArray;
                            string temp = string.Join("", ToBeContinued).Replace("\"\"", "");
                            int quaotCount = temp.Count(c => c == '"');
                            if (ToBeContinued.GetLength(0) >= Cols && quaotCount % 2 == 0 )
                            {
                                Fields = ToBeContinued;
                                ToBeContinued = new string[] { };
                                lineToBeContinued = false;
                            }
                            else
                            {
                                lineToBeContinued = true;
                                continue;
                            }
                        }

                        //modified by Teemo @2016 09 13
                        //handle ',' and '"'
                        //Deserialize CSV following Excel's rule:
                        // 1: If there is commas in a field, quote the field.
                        // 2: Two consecutive quotes indicate a user's quote.

                        List<int> singleLeftquota = new List<int>();
                        List<int> singleRightquota = new List<int>();

                        //combine fileds if number of commas match
                        if (Fields.GetLength(0) > Cols) 
                        {
                            bool lastSingleQuoteIsLeft = true;
                            for (int j = 0; j < Fields.GetLength(0); j++)
                            {
                                bool leftOddquota = false;
                                bool rightOddquota = false;
                                if (Fields[j].StartsWith("\"")) 
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    foreach (char c in Fields[j]) //start with how many "
                                    {
                                        if (c == '"')
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }
                                    if (numberOfConsecutiveQuotes % 2 == 1)//start with odd number of quotes indicate system quote
                                    {
                                        leftOddquota = true;
                                    }
                                }

                                if (Fields[j].EndsWith("\""))
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    for (int jj = Fields[j].Length - 1; jj >= 0; jj--)
                                    {
                                        if (Fields[j].Substring(jj,1) == "\"") // end with how many "
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }

                                    if (numberOfConsecutiveQuotes % 2 == 1)//end with odd number of quotes indicate system quote
                                    {
                                        rightOddquota = true;
                                    }
                                }
                                if (leftOddquota && !rightOddquota)
                                {
                                    singleLeftquota.Add(j);
                                    lastSingleQuoteIsLeft = true;
                                }
                                else if (!leftOddquota && rightOddquota)
                                {
                                    singleRightquota.Add(j);
                                    lastSingleQuoteIsLeft = false;
                                }
                                else if (Fields[j] == "\"") //only one quota in a field
                                {
                                    if (lastSingleQuoteIsLeft)
                                    {
                                        singleRightquota.Add(j);
                                    }
                                    else
                                    {
                                        singleLeftquota.Add(j);
                                    }
                                }
                            }
                            if (singleLeftquota.Count == singleRightquota.Count)
                            {
                                int insideCommas = 0;
                                for (int indexN = 0; indexN < singleLeftquota.Count; indexN++)
                                {
                                    insideCommas += singleRightquota[indexN] - singleLeftquota[indexN];
                                }
                                if (Fields.GetLength(0) - Cols >= insideCommas) //probabaly matched
                                {
                                    int validFildsCount = insideCommas + Cols; //(Fields.GetLength(0) - insideCommas) may be exceed the Cols
                                    String[] temp = new String[validFildsCount];
                                    int totalOffSet = 0;
                                    for (int iii = 0; iii < validFildsCount - totalOffSet; iii++)
                                    {
                                        bool combine = false;
                                        int storedIndex = 0;
                                        for (int iInLeft = 0; iInLeft < singleLeftquota.Count; iInLeft++)
                                        {
                                            if (iii + totalOffSet == singleLeftquota[iInLeft])
                                            {
                                                combine = true;
                                                storedIndex = iInLeft;
                                                break;
                                            }
                                        }
                                        if (combine)
                                        {
                                            int offset = singleRightquota[storedIndex] - singleLeftquota[storedIndex];
                                            for (int combineI = 0; combineI <= offset; combineI++)
                                            {
                                                temp[iii] += Fields[iii + totalOffSet + combineI] + ",";
                                            }
                                            temp[iii] = temp[iii].Remove(temp[iii].Length - 1, 1);
                                            totalOffSet += offset;
                                        }
                                        else
                                        {
                                            temp[iii] = Fields[iii + totalOffSet];
                                        }
                                    }
                                    Fields = temp;
                                }
                            }
                        }
                        Row = dt.NewRow();
                        for (int f = 0; f < Cols; f++)
                        {
                            Fields[f] = Fields[f].Replace("\"\"", "\""); //Two consecutive quotes indicate a user's quote
                            if (Fields[f].StartsWith("\""))
                            {
                                if (Fields[f].EndsWith("\""))
                                {
                                    Fields[f] = Fields[f].Remove(0, 1);
                                    if (Fields[f].Length > 0)
                                    {
                                        Fields[f] = Fields[f].Remove(Fields[f].Length - 1, 1);
                                    }
                                }
                            }
                            Row[f] = Fields[f];
                        }
                        dt.Rows.Add(Row);
                        rowcount++;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception( "row: " + (rowcount+2) + ", " + ex.Message);
            }
            //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", FilePath + FileName));
            //OleDbCommand command = new OleDbCommand("SELECT * FROM " + FileName, connection);
            //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
            //DataTable dt = new DataTable();
            //adapter.Fill(dt);
            //adapter.Dispose();
            return dt;
        }
        else
            return null;

        //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", strFilePath));
        //OleDbCommand command = new OleDbCommand("SELECT * FROM " + strFileName, connection);
        //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
        //DataTable dt = new DataTable();
        //adapter.Fill(dt);
        //return dt;
    }
Teemo
quelle
0
 Public Function ReadCsvFileToDataTable(strFilePath As String) As DataTable
    Dim dtCsv As DataTable = New DataTable()
    Dim Fulltext As String
    Using sr As StreamReader = New StreamReader(strFilePath)
        While Not sr.EndOfStream
            Fulltext = sr.ReadToEnd().ToString()
            Dim rows As String() = Fulltext.Split(vbLf)
            For i As Integer = 0 To rows.Count() - 1 - 1
                Dim rowValues As String() = rows(i).Split(","c)
                If True Then
                    If i = 0 Then
                        For j As Integer = 0 To rowValues.Count() - 1
                            dtCsv.Columns.Add(rowValues(j))
                        Next
                    Else
                        Dim dr As DataRow = dtCsv.NewRow()
                        For k As Integer = 0 To rowValues.Count() - 1
                            dr(k) = rowValues(k).ToString()
                        Next
                        dtCsv.Rows.Add(dr)
                    End If
                End If
            Next
        End While
    End Using
    Return dtCsv
End Function
Esat Kiziltepe
quelle
0

Ich habe kürzlich einen CSV-Parser für .NET geschrieben, von dem ich behaupte, dass er derzeit der schnellste ist , der als Nuget-Paket verfügbar ist: Sylvan.Data.Csv .

Die Verwendung dieser Bibliothek zum Laden von a DataTableist äußerst einfach.

using var tr = File.OpenText("data.csv");
using var dr = CsvDataReader.Create(tr);
var dt = new DataTable();
dt.Load(dr);

Angenommen, Ihre Datei ist eine durch Kommas getrennte Standarddatei mit Überschriften, das ist alles, was Sie brauchen. Es gibt auch Optionen zum Lesen von Dateien ohne Header und zum Verwenden alternativer Trennzeichen usw.

Es ist auch möglich, ein benutzerdefiniertes Schema für die CSV-Datei bereitzustellen, damit Spalten als etwas anderes als stringWerte behandelt werden können. Auf diese Weise können die DataTableSpalten mit Werten geladen werden, mit denen einfacher gearbeitet werden kann, da Sie sie beim Zugriff nicht erzwingen müssen.

var schema = new TypedCsvSchema();
schema.Add(0, typeof(int));
schema.Add(1, typeof(string));
schema.Add(2, typeof(double?));
schema.Add(3, typeof(DateTime));
schema.Add(4, typeof(DateTime?));

var options = new CsvDataReaderOptions { 
    Schema = schema 
};

using var tr = GetData();
using var dr = CsvDataReader.Create(tr, options);

TypedCsvSchemaist eine Implementierung, ICsvSchemaProviderdie eine einfache Möglichkeit bietet, die Arten der Spalten zu definieren. Es ist jedoch auch möglich, eine benutzerdefinierte Funktion bereitzustellen, ICsvSchemaProviderwenn Sie mehr Metadaten bereitstellen möchten, z. B. Eindeutigkeit oder eingeschränkte Spaltengröße usw.

MarkPflug
quelle