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

266

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

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

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

David Pfeffer
la source

Réponses:

138

Laissez une bibliothèque gérer tous les petits détails pour vous! :-)

Consultez FileHelpers et restez au sec - ne vous répétez pas - pas besoin de réinventer la roue une gazillionième fois ....

Il vous suffit simplement de définir cette forme de vos données - les champs de votre ligne individuelle dans le CSV - au moyen d'une classe publique (et d'attributs bien pensés comme les valeurs par défaut, les remplacements des valeurs NULL et ainsi de suite), le point le moteur FileHelpers dans un fichier, et bingo - vous récupérez toutes les entrées de ce fichier. Une opération simple - de grandes performances!

marc_s
la source
1
jusqu'à ce que vous ayez besoin de quelque chose de vraiment personnalisé (et la plupart de cela peut être implémenté sous forme d'extensions de toute façon) FileHelpers est de loin la meilleure solution, vraiment pratique, testée et performante
mikus
3
Depuis le 1er juin 2015, le seul moyen de télécharger FileHelpers était de le rechercher sur sourceforge.net. Voici le lien utilisé: sourceforge.net/projects/filehelpers/?source=directory
Sudhanshu Mishra
2
@dotnetguy nous sommes sur le point de sortir la version 3.1 (actuellement 3.1-rc2). Nous avons également repensé le site: www.filehelpers.net, vous pouvez télécharger la dernière version à partir de là
Marcos Meli
1
@MarcosMeli merci beaucoup! J'ai déjà utilisé FileHelpers dans l'un de mes projets et c'était un jeu d'enfant - bravo à l'équipe. Je prévois bientôt un blog à ce sujet et btw - J'adore le nouveau site - bravo!
Sudhanshu Mishra du
FileHelpers ne gère pas correctement les virgules entre guillemets dans les CSV, ni mappe réellement les en-têtes de champ, s'attendant à la place que les colonnes soient dans le même ordre que les champs sont déclarés dans votre type. Je ne l'utiliserais pas personnellement.
Alastair Maw
358

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

Ajoutez une référence à Microsoft.VisualBasic.dll (fonctionne très bien en C #, ne vous occupez pas du nom)

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

Les documents sont ici - Classe TextFieldParser

PS Si vous avez besoin d'un exportateur CSV , essayez CsvExport (divulgation: je suis l'un des contributeurs)

Alex
la source
2
D'après mon expérience, TextFieldParser ne fonctionne pas bien avec des fichiers volumineux (par exemple> 250 Mo). :(
MBoros
6
TextFieldParser implémente IDisposable, il peut donc être préférable de l'utiliser dans une clause using. Bonne réponse sinon.
Chris Bush
3
Dans le constructeur, vous voudrez peut-être utiliser un encodage différent de celui par défaut, comme ceci: nouveau TextFieldParser ("c: \ temp \ test.csv", System.Text.Encoding.UTF8)
neural5torm
1
Notez que si un champ de votre CSV contient des lignes vides, elles seront ignorées TextFieldParser.ReadLine(). Voir les documents TextFieldParser
mcNux
3
Existe-t-il un moyen d'obtenir cela dans .NET Core?
Hugo Zink
183

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

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

Parfois, vous ne possédez pas les objets dans lesquels vous essayez de lire. Dans ce cas, vous pouvez utiliser un mappage fluide car vous ne pouvez pas attribuer d'attributs à la classe.

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

ÉDITER:

CsvReader nécessite désormais que CultureInfo soit passé dans le constucteur ( https://github.com/JoshClose/CsvHelper/issues/1441 ).

Exemple:

var csv = new CsvReader(File.OpenText("file.csv"), System.Globalization.CultureInfo.CurrentCulture);
Josh Close
la source
18
Je suis d'accord avec @ kubal5003. Ce qui m'a vendu, c'est que vous l'avez disponible sous forme de package NuGet. Merci mec, c'est rapide, et fait toute la lecture csv dont j'ai besoin.
Gromer
7
C'est sacrément rapide. 1,3 million d'enregistrements lus et désérialisés en 10 secondes.
marisks
2
Grande bibliothèque très facile à implémenter. Je suggère simplement à Josh de mettre à jour sa réponse ici parce que la bibliothèque a un peu changé depuis que cette réponse a été écrite et vous ne pouvez plus instancier CsvHelper (ce n'est plus qu'un espace de noms maintenant) mais vous devez utiliser la classe CsvReader.
Marko
1
CsvClassMap ne semble pas exister dans la dernière version de CsvHelper?
knocte
1
knocte, il s'appelle ClassMap maintenant. Il y a aussi d'autres changements, comme devoir faire une lecture avant de demander l'enregistrement d'en-tête (qui, soit dit en passant, est réglé sur tout ce qui a été lu par le premier appel à Read ()). Comme d'autres l'ont déjà mentionné, il est très rapide et facile à utiliser.
norgie
31

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 une analyse comparative 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;
    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();
    }
}

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

alexn
la source
12

Si vous avez seulement besoin de lire des fichiers csv, je 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 open source.

Giorgi
la source
FileHelpers a un résumé attrayant: filehelpers.com Les FileHelpers sont une bibliothèque .NET gratuite et facile à utiliser pour importer / exporter des données à partir d'une longueur fixe ou des enregistrements délimités dans des fichiers, des chaînes ou des flux.
AnneTheAgile
Bien que ce lien puisse répondre à la question, les réponses de lien uniquement sont déconseillées sur Stack Overflow, vous pouvez améliorer cette réponse en prenant des parties vitales du lien et en l'intégrant dans votre réponse, cela garantit que votre réponse est toujours une réponse si le lien est modifié ou supprimé :)
WhatsThePoint
11

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

J'utilise ceci pour la simplicité de le porter dans des projets prêts à l'emploi:

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

Et utilisez-le comme:

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

[Aide csv mise à jour: bug corrigé où le dernier nouveau caractère de ligne créait une nouvelle ligne]

Base33
la source
17
si l'une des entrées csv contient une virgule (,) ce code ne fonctionnera pas.
hakan
Pour garder les choses légères, j'ai utilisé un caractère de pipe comme séparateur. «|»
Base33
excellente solution. Juste une question sur le 2e extrait. Quel type d'objet est Person
Cocoa Dev
@CocoaDev C'est une classe qui contient deux propriétés de chaîne - Name et TelephoneNo. Mais uniquement pour l'exemple. Si l'une des propriétés était un entier, il devrait s'agir simplement d'une conversion directe (avec vérification?).
Base33
10

Cette solution utilise l' assembly officiel Microsoft.VisualBasic pour analyser CSV.

Avantages:

  • délimiteur s'échappant
  • ignore l'en-tête
  • rogner les espaces
  • ignorer les commentaires

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
la source
7

J'ai écrit TinyCsvParser pour .NET, qui est l'un des analyseurs .NET les plus rapides et hautement configurable pour analyser 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

Imaginez que nous ayons une liste de personnes dans un fichier CSV persons.csvavec leur prénom, nom et 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 pourrait ressembler à ceci.

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

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

private class CsvPersonMapping : CsvMapping<Person>
{

    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 analyser les données CSV avec a 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);
        }
    }
}

Mode d'emploi

Un guide d'utilisation complet est disponible à l'adresse suivante:

bytefish
la source
1

Voici mon implémentation KISS ...

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 Begun
la source
1
Cela ne concerne pas les sauts de ligne dans les chaînes entre guillemets qui sont valides dans un fichier CSV.
John Leidegren
Alex, ce que John essaie de dire, c'est que la RFC 4180 ( ietf.org/rfc/rfc4180.txt - voir la section 2 et le point 6) permet à une colonne d'avoir un CR LF au milieu d'une colonne la répartissant efficacement sur 2 lignes dans un fichier. Votre solution fonctionnera probablement bien dans la plupart des cas (surtout si les fichiers CSV ont été créés en enregistrant à partir d'Excel), mais elle ne couvre pas ce cas de bord. CsvHelper, mentionné ci-dessus, est censé prendre ce cas en considération.
David Yates du
Oui, c'est vrai, mais si vous avez CR LF dans votre CSV, vous ne devriez probablement pas utiliser CSV, mais quelque chose de plus approprié comme json ou xml, ou un format de longueur fixe.
Alex a commencé
1

Il y a quelque temps, j'avais écrit une classe simple pour la lecture / écriture CSV basée sur la Microsoft.VisualBasicbibliothèque. 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'en-tête, il vous suffit de lire les csv.Rows[0]cellules :)

Andrew
la source
1

Solution de fichier source unique pour des besoins d'analyse simples, utile. Traite tous les cas de bord méchant. Telles que la normalisation de nouvelles lignes et la gestion de nouvelles lignes dans des 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 des colonnes (et de calculer les index des colonnes) à partir de la première ligne. Aussi simple que cela.

Notez qu'il Dumps'agit d'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 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
la source
1

Un autre de cette liste, 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 objet POCO requis)

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

L'exemple ci-dessous montre comment lire un fichier CSV à l'aide d'un objet POCO

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

Veuillez consulter les articles de CodeProject sur la façon de l'utiliser.

RajN
la source
0

Basé sur la publication d'unlimit sur Comment diviser correctement un CSV en utilisant la fonction C # split ()? :

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

REMARQUE: cela ne gère pas les virgules d'échappement / imbriquées, etc., et ne convient donc qu'à certaines listes CSV simples.

radsdau
la source
2
C'est très mauvais et probablement lent :)
EKS
1
Probablement, mais cela fonctionne parfaitement et simplement pour un petit ensemble de paramètres, c'est donc une solution valide et utile. Pourquoi voter contre? "Very Bad" est un peu extrême, tu ne trouves pas?
radsdau
1
Il ne gère pas les virgules échappées / imbriquées, etc. Fonctionne dans certains cas mais ne fonctionnera certainement pas pour tous les fichiers csv
NStuke
Vous avez raison; Je vais modifier la réponse pour refléter cela. Merci. Mais il a toujours sa place.
radsdau
Cela a parfaitement fonctionné pour mon cas d'utilisation où je construis une dll de clr de serveur sql et ne peux utiliser aucun de ces autres packages externes. J'avais juste besoin d'analyser un simple fichier csv avec un nom de fichier et un nombre de lignes.
dubvfan87
0

Ce code lit csv à 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
la source
1
TextFieldParser se trouve dans Microsoft.VisualBasic.dll.
user3285954
0

Si quelqu'un veut un extrait, il peut plonger dans son code sans avoir à lier une bibliothèque ou télécharger un package. Voici une version que j'ai écrite:

    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
la source
0

Voici une solution courte et simple.

                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);
                        }
                    }
Le faucon
la source