Analyse des fichiers CSV en C #, avec en-tête

Existe-t-il une méthode par défaut / officielle / recommandée pour parsingr les fichiers CSV en C #? Je ne veux pas lancer mon propre parsingur.

En outre, j’ai vu des cas de personnes utilisant ODBC / OLE DB pour lire le format CSV via le pilote de texte, et beaucoup de gens découragent cela en raison de ses “inconvénients”. Quels sont ces inconvénients?

Idéalement, je cherche un moyen de lire le fichier CSV par nom de colonne, en utilisant le premier enregistrement comme nom d’en-tête / champ. Certaines des réponses données sont correctes mais fonctionnent pour désérialiser le fichier en classes.

Laissez une bibliothèque gérer tous les détails les plus difficiles pour vous! 🙂

Découvrez FileHelpers et restz au sec – Ne répétez pas vous-même – pas besoin de réinventer la roue une troisième fois …

Vous devez simplement définir cette forme de vos données – les champs de votre ligne individuelle dans le fichier CSV – au moyen d’une classe publique (et des atsortingbuts bien pensés tels que les valeurs par défaut, les remplacements pour les valeurs NULL, etc.). le moteur FileHelpers dans un fichier, et le bingo – vous récupérez toutes les entrées de ce fichier. Une opération simple – une excellente performance!

Un parsingur CSV fait désormais partie de .NET Framework.

Ajouter une référence à Microsoft.VisualBasic.dll (fonctionne correctement en C #, ne vous souciez pas du nom)

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

Les documents sont ici – Classe TextFieldParser

CsvHelper (une bibliothèque que je gère) lira un fichier CSV dans des objects personnalisés.

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

Parfois, vous ne possédez pas les objects que vous essayez de lire. Dans ce cas, vous pouvez utiliser un mappage fluide car vous ne pouvez pas atsortingbuer d’atsortingbuts à la classe.

 public sealed class MyCustomObjectMap : CsvClassMap { 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(); } } 

Dans une application métier, j’utilise le projet Open Source sur codeproject.com , CSVReader .

Cela fonctionne bien et a de bonnes performances. Il y a des parsings comparatives sur le lien que j’ai fourni.

Un exemple simple, copié depuis la page du projet:

 using (CsvReader csv = new CsvReader(new StreamReader("data.csv"), true)) { int fieldCount = csv.FieldCount; ssortingng[] 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(); } } 

Comme vous pouvez le voir, il est très facile de travailler avec.

Je sais que c’est un peu tard, mais juste trouvé une bibliothèque Microsoft.VisualBasic.FileIO qui a la classe TextFieldParser pour traiter les fichiers CSV.

Si vous avez seulement besoin de lire des fichiers csv, je vous recommande cette bibliothèque: Un lecteur CSV rapide
Si vous devez également générer des fichiers csv, utilisez celui-ci: FileHelpers

Les deux sont gratuits et opensource.

Voici une classe d’assistance que j’utilise souvent, au cas où quelqu’un reviendrait sur ce sujet (je voulais le partager).

Je l’utilise pour la simplicité du portage dans des projets prêts à l’emploi:

 public class CSVHelper : List { protected ssortingng csv = ssortingng.Empty; protected ssortingng separator = ","; public CSVHelper(ssortingng csv, ssortingng separator = "\",\"") { this.csv = csv; this.separator = separator; foreach (ssortingng line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !ssortingng.IsNullOrEmpty(s))) { ssortingng[] values = Regex.Split(line, separator); for (int i = 0; i < values.Length; i++) { //Trim values values[i] = values[i].Trim('\"'); } this.Add(values); } } } 

Et l'utiliser comme:

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

[Assistant csv mis à jour: bogue corrigé où le dernier nouveau caractère de ligne créait une nouvelle ligne]

Cette solution utilise l’assembly officiel Microsoft.VisualBasic pour parsingr CSV.

Avantages:

  • délimiteur échappant
  • ignore l’en-tête
  • espaces de garniture
  • ignorer les commentaires

Code:

  using Microsoft.VisualBasic.FileIO; public static List> ParseCSV (ssortingng csv) { List> result = new List>(); // To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project. using (TextFieldParser parser = new TextFieldParser(new SsortingngReader(csv))) { parser.CommentTokens = new ssortingng[] { "#" }; parser.SetDelimiters(new ssortingng[] { ";" }); parser.HasFieldsEnclosedInQuotes = true; // Skip over header line. //parser.ReadLine(); while (!parser.EndOfData) { var values = new List(); var readFields = parser.ReadFields(); if (readFields != null) values.AddRange(readFields); result.Add(values); } } return result; } 

J’ai écrit TinyCsvParser pour .NET, qui est l’un des parsingurs .NET les plus rapides du monde et hautement configurable pour parsingr presque tous les formats CSV.

Il est publié sous licence MIT:

Vous pouvez utiliser NuGet pour l’installer. Exécutez la commande suivante dans la console du gestionnaire de packages .

 PM> Install-Package TinyCsvParser 

Usage

Imaginons que nous ayons une liste de personnes dans un fichier CSV persons.csv avec leur prénom, leur nom et leur date de naissance.

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

Le modèle de domaine correspondant dans notre système peut ressembler à ceci.

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

Lorsque vous utilisez TinyCsvParser, vous devez définir le mappage entre les colonnes des données CSV et la propriété dans votre modèle de domaine.

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

Et puis, nous pouvons utiliser le mappage pour parsingr les données CSV avec un CsvParser .

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

Mode d’emploi

Un guide de l’utilisateur complet est disponible à l’adresse suivante:

Je ne connais aucune manière officielle, mais vous devez en effet utiliser les bibliothèques existantes. En voici un que j’ai trouvé très utile dans CodeProject:

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

Voici ma mise en œuvre de KISS …

 using System; using System.Collections.Generic; using System.Text; class CsvParser { public static List Parse(ssortingng line) { const char escapeChar = '"'; const char splitChar = ','; bool inEscape = false; bool priorEscape = false; List result = new List(); SsortingngBuilder sb = new SsortingngBuilder(); 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.ToSsortingng()); return result; } } 

Il y a quelque temps, j’avais écrit une classe simple pour la lecture / écriture CSV basée sur la bibliothèque Microsoft.VisualBasic . En utilisant cette classe simple, vous pourrez travailler avec CSV comme avec un tableau à 2 dimensions. Vous pouvez trouver ma classe par le lien suivant: https://github.com/ukushu/DataExporter

Exemple simple d’utilisation:

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

Pour lire l’entête, il suffit de lire les csv.Rows[0] 🙂

Basé sur le post de unlimit sur Comment diviser correctement un CSV en utilisant la fonction C # split ()? :

 ssortingng[] tokens = System.Text.RegularExpressions.Regex.Split(paramSsortingng, ","); 

REMARQUE: cela ne gère pas les virgules échappées / nestedes, etc., et ne convient donc que pour certaines listes CSV simples.

Ce code lit csv à DataTable:

 public static DataTable ReadCsv(ssortingng path) { DataTable result = new DataTable("SomeData"); using (TextFieldParser parser = new TextFieldParser(path)) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); bool isFirstRow = true; //IList headers = new List(); while (!parser.EndOfData) { ssortingng[] fields = parser.ReadFields(); if (isFirstRow) { foreach (ssortingng field in fields) { result.Columns.Add(new DataColumn(field, typeof(ssortingng))); } isFirstRow = false; } else { int i = 0; DataRow row = result.NewRow(); foreach (ssortingng field in fields) { row[i++] = field; } result.Rows.Add(row); } } } return result; } 

Cinchoo ETL – une bibliothèque open source pour lire et écrire plusieurs formats de fichiers (CSV, fichier plat, Xml, JSON, etc.)

L’exemple ci-dessous montre comment lire rapidement un fichier CSV (aucun object POCO requirejs)

 static void ReadCSV() { using (var stream = new MemoryStream()) using (var reader = new StreamReader(stream)) using (var writer = new StreamWriter(stream)) using (var parser = new ChoCSVReader(reader)) { writer.WriteLine("id,name"); writer.WriteLine("1,Carl"); writer.WriteLine("2,Mark"); writer.WriteLine("3,Tom"); writer.Flush(); stream.Position = 0; foreach (dynamic dr in parser) { Console.WriteLine("Id: {0}, Name: {1}", dr.id, dr.name); } } } 

L’exemple ci-dessous montre comment lire le fichier CSV en utilisant l’object POCO

 public partial class EmployeeRec { public int Id { get; set; } public ssortingng Name { get; set; } } static void ReadCSV() { using (var stream = new MemoryStream()) using (var reader = new StreamReader(stream)) using (var writer = new StreamWriter(stream)) using (var parser = new ChoCSVReader(reader)) { writer.WriteLine("id,name"); writer.WriteLine("1,Carl"); writer.WriteLine("2,Mark"); writer.WriteLine("3,Tom"); writer.Flush(); stream.Position = 0; foreach (var dr in parser) { Console.WriteLine("Id: {0}, Name: {1}", dr.id, dr.name); } } } 

S’il vous plaît vérifier les articles à CodeProject sur la façon de l’utiliser.

Solution de fichier source unique pour des besoins d’parsing simples, utiles. Traite de tous les cas de bord désagréables. Comme la nouvelle normalisation des lignes et la gestion des nouvelles lignes dans les littéraux de chaîne entre guillemets. Vous êtes le bienvenu!

Si votre fichier CSV a un en-tête, il vous suffit de lire les noms de colonne (et les index de colonne de calcul) de la première ligne. Aussi simple que cela.

Notez que Dump est une méthode LINQPad, vous souhaiterez peut-être la supprimer si vous n’utilisez pas LINQPad.

 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 ssortingng[] Row; public ssortingng this[int index] => Row[index]; public Record(ssortingng[] row) { Row = row; } } public static List ParseText(ssortingng text) { return Parse(new SsortingngReader(text)); } public static List ParseFile(ssortingng fn) { using (var reader = File.OpenText(fn)) { return Parse(reader); } } public static List Parse(TextReader reader) { var data = new List(); var col = new SsortingngBuilder(); var row = new List(); 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(ssortingng s, SsortingngBuilder col, List 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.ToSsortingng().Trim()); col.Length = 0; } return true; } public static bool TokenizeQuote(ssortingng s, ref int i, SsortingngBuilder col, List 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; } }