Lettura di file CSV in C #

Qualcuno sa di una libreria open-source che ti permette di analizzare e leggere i file .csv in C #?

Dai un’occhiata a A Fast CSV Reader su CodeProject.

Qui, scritto da voi veramente per usare collezioni generiche e blocchi iteratori. Supporta campi di testo racchiusi in virgolette doppie (compresi quelli che si estendono su più linee) utilizzando la convenzione a doppio escape (così "" all’interno di un campo quotato legge come carattere a virgoletta singola). Non supporta:

  • Testo racchiuso tra virgolette singole
  • \ -escluso testo citato
  • delimitatori alternativi (non funzionerà ancora sui campi delimitati da pipe o tabulazioni)
  • Campi di testo non quotati che iniziano con una citazione

Ma tutti questi sarebbero abbastanza facili da aggiungere se ne hai bisogno. Non l’ho messo in benchmark ovunque (mi piacerebbe vedere alcuni risultati), ma le prestazioni dovrebbero essere molto buone – meglio di qualsiasi altra cosa .Split() basata comunque.

Adesso su GitHub

Aggiornamento : sentivo come aggiungere il supporto di testo racchiuso tra virgolette. È un semplice cambiamento, ma l’ho digitato direttamente nella finestra di risposta, quindi non è stato verificato. Usa il link di revisione in basso se preferisci il vecchio codice (testato).

 public static class CSV { public static IEnumerable> FromFile(string fileName) { foreach (IList item in FromFile(fileName, ignoreFirstLineDefault)) yield return item; } public static IEnumerable> FromFile(string fileName, bool ignoreFirstLine) { using (StreamReader rdr = new StreamReader(fileName)) { foreach(IList item in FromReader(rdr, ignoreFirstLine)) yield return item; } } public static IEnumerable> FromStream(Stream csv) { foreach (IList item in FromStream(csv, ignoreFirstLineDefault)) yield return item; } public static IEnumerable> FromStream(Stream csv, bool ignoreFirstLine) { using (var rdr = new StreamReader(csv)) { foreach (IList item in FromReader(rdr, ignoreFirstLine)) yield return item; } } public static IEnumerable> FromReader(TextReader csv) { //Probably should have used TextReader instead of StreamReader foreach (IList item in FromReader(csv, ignoreFirstLineDefault)) yield return item; } public static IEnumerable> FromReader(TextReader csv, bool ignoreFirstLine) { if (ignoreFirstLine) csv.ReadLine(); IList result = new List(); StringBuilder curValue = new StringBuilder(); char c; c = (char)csv.Read(); while (csv.Peek() != -1) { switch (c) { case ',': //empty field result.Add(""); c = (char)csv.Read(); break; case '"': //qualified text case '\'': char q = c; c = (char)csv.Read(); bool inQuotes = true; while (inQuotes && csv.Peek() != -1) { if (c == q) { c = (char)csv.Read(); if (c != q) inQuotes = false; } if (inQuotes) { curValue.Append(c); c = (char)csv.Read(); } } result.Add(curValue.ToString()); curValue = new StringBuilder(); if (c == ',') c = (char)csv.Read(); // either ',', newline, or endofstream break; case '\n': //end of the record case '\r': //potential bug here depending on what your line breaks look like if (result.Count > 0) // don't return empty records { yield return result; result = new List(); } c = (char)csv.Read(); break; default: //normal unqualified text while (c != ',' && c != '\r' && c != '\n' && csv.Peek() != -1) { curValue.Append(c); c = (char)csv.Read(); } result.Add(curValue.ToString()); curValue = new StringBuilder(); if (c == ',') c = (char)csv.Read(); //either ',', newline, or endofstream break; } } if (curValue.Length > 0) //potential bug: I don't want to skip on a empty column in the last record if a caller really expects it to be there result.Add(curValue.ToString()); if (result.Count > 0) yield return result; } private static bool ignoreFirstLineDefault = false; } 

L’ ultima volta che è stata fatta questa domanda , ecco la risposta che ho dato:

Se stai solo provando a leggere un file CSV con C #, la cosa più semplice è usare la class Microsoft.VisualBasic.FileIO.TextFieldParser . In realtà è incorporato in .NET Framework, anziché essere un’estensione di terze parti.

Sì, è in Microsoft.VisualBasic.dll , ma ciò non significa che non puoi usarlo da C # (o da qualsiasi altro linguaggio CLR).

Ecco un esempio di utilizzo, tratto dalla documentazione MSDN :

 Using MyReader As New _ Microsoft.VisualBasic.FileIO.TextFieldParser("C:\testfile.txt") MyReader.TextFieldType = FileIO.FieldType.Delimited MyReader.SetDelimiters(",") Dim currentRow As String() While Not MyReader.EndOfData Try currentRow = MyReader.ReadFields() Dim currentField As String For Each currentField In currentRow MsgBox(currentField) Next Catch ex As Microsoft.VisualBasic.FileIO.MalformsdLineException MsgBox("Line " & ex.Message & _ "is not valid and will be skipped.") End Try End While End Using 

Ancora, questo esempio è in VB.NET, ma sarebbe banale tradurlo in C #.

Mi piace molto la libreria FileHelpers . È veloce, è C # 100%, è disponibile GRATUITAMENTE , è molto flessibile e facile da usare.

Sto implementando la risposta di Daniel Pryden in C #, quindi è più facile da tagliare e incollare e personalizzare. Penso che questo sia il metodo più semplice per analizzare i file CSV. Basta aggiungere un riferimento e sei praticamente finito.

Aggiungi il riferimento Microsoft.VisualBasic al tuo progetto

Poi ecco il codice di esempio in C # dalla risposta di Joel:

 using (Microsoft.VisualBasic.FileIO.TextFieldParser MyReader = new Microsoft.VisualBasic.FileIO.TextFieldParser(filename)) { MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited; MyReader.SetDelimiters(","); while (!MyReader.EndOfData) { try { string[] fields = MyReader.ReadFields(); if (first) { first = false; continue; } // This is how I treat my data, you'll need to throw this out. //"Type" "Post Date" "Description" "Amount" LineItem li = new LineItem(); li.date = DateTime.Parse(fields[1]); li.description = fields[2]; li.Value = Convert.ToDecimal(fields[3]); lineitems1.Add(li); } catch (Microsoft.VisualBasic.FileIO.MalformsdLineException ex) { MessageBox.Show("Line " + ex.Message + " is not valid and will be skipped."); } } } 

Oltre all’analisi / lettura, alcune librerie fanno altre cose carine come convertire i dati analizzati in oggetti per te.

Ecco un esempio dell’uso di CsvHelper (una libreria che gestisco) per leggere un file CSV in oggetti.

 var csv = new CsvHelper( File.OpenRead( "file.csv" ) ); var myCustomObjectList = csv.Reader.GetRecords(); 

Per impostazione predefinita, le convenzioni vengono utilizzate per la corrispondenza delle intestazioni / colonne con le proprietà. È ansible modificare il comportamento modificando le impostazioni.

 // Using attributes: public class MyCustomObject { [CsvField( Name = "First Name" )] public string StringProperty { get; set; } [CsvField( Index = 0 )] public int IntProperty { get; set; } [CsvField( Ignore = true )] public string ShouldIgnore { get; set; } } 

A volte non “possiedi” l’object con cui vuoi popolare i dati. In questo caso è ansible utilizzare la mapping della class fluente.

 // Fluent class mapping: public sealed class MyCustomObjectMap : CsvClassMap { public MyCustomObjectMap() { Map( m => m.StringProperty ).Name( "First Name" ); Map( m => m.IntProperty ).Index( 0 ); Map( m => m.ShouldIgnore ).Ignore(); } } 

È ansible utilizzare Microsoft.VisualBasic.FileIO.TextFieldParser

ottenere sotto l’esempio di codice dall’articolo di cui sopra

 static void Main() { string [email protected]"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; }