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

y a-t-il un moyen par défaut/officiel/recommandé pour analyser les fichiers CSV dans C#? Je ne veux pas lancer mon propre analyseur.

aussi, j'ai vu des cas de personnes utilisant ODBC/OLE DB pour lire 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 CSV par nom de colonne, en utilisant le premier enregistrement comme noms d'en-tête / de champ. Certains de la les réponses données sont correctes mais fondamentalement désérialiser le fichier en cours.

211
demandé sur smci 2010-01-17 18:21:16

16 réponses

laissez une bibliothèque s'occuper de tous les détails nitty-gritty pour vous! :- )

Check out FileHelpers et rester au SEC - Ne Vous Répétez pas - pas besoin de ré-inventer la roue, un gazillionth temps....

vous avez essentiellement juste besoin de définir cette forme de vos données - les champs dans votre ligne individuelle dans le CSV - au moyen d'une classe publique (et si bien pensé attributs comme les valeurs par défaut, remplacements pour les valeurs nulles et so forth), pointer le moteur FileHelpers à un fichier, et bingo - vous obtenez toutes les entrées de ce fichier. Une opération simple - grande performance!

117
répondu marc_s 2015-07-23 04:34:23

un analyseur CSV fait maintenant partie du .net Framework.

ajouter une référence à Microsoft.VisualBasic.dll (fonctionne très bien 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
        string[] fields = parser.ReadFields();
        foreach (string field in fields)
        {
            //TODO: Process field
        }
    }
}

Les docs sont ici - TextFieldParser Classe

273
répondu Alex 2016-02-08 17:59:06

CsvHelper (une bibliothèque que je maintiens) 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 que vous essayez de lire. Dans ce cas, vous pouvez utiliser la cartographie fluente parce que vous ne pouvez pas mettre les attributs sur 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>();
    }
}
155
répondu Josh Close 2017-11-30 03:22:07

dans une application d'affaires, j'utilise le projet Open Source sur codeproject.com, CSVReader .

il fonctionne bien, et a une bonne performance. Il y a une analyse comparative sur le lien que j'ai fourni.

un exemple simple, tiré de 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.

30
répondu alexn 2010-01-17 15:24:38

je sais que c'est un peu tard mais je viens de trouver une bibliothèque Microsoft.VisualBasic.FileIO qui a la classe TextFieldParser pour traiter les fichiers csv.

17
répondu user1131926 2016-05-11 20:34:42

si vous n'avez besoin que de lire les fichiers csv, alors je recommande cette bibliothèque: un lecteur CSV rapide

si vous devez aussi générer des fichiers csv, utilisez celui-ci: FileHelpers

les deux sont libres et opensource.

11
répondu Giorgi 2015-07-22 23:22:45

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

Je l'utilise pour la simplicité de le transférer 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 l'utiliser 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;
}

[mise à jour de csv helper: bug corrigé lorsque le dernier nouveau caractère de ligne a créé une nouvelle ligne]

9
répondu Base33 2012-12-10 13:43:39

Cette solution utilise le Microsoft officiel.Assemblage de VisualBasic pour analyser CSV.

avantages:

  • delimiter escapade
  • ignore Header
  • trim 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;
    }
8
répondu Jonas_Hess 2016-07-27 12:48:02

j'ai écrit TinyCsvParser pour .NET, qui est l'un des analyseurs .NET les plus rapides et hautement configurable pour analyser presque n'importe quel format CSV.

Il est publié sous Licence MIT:

vous pouvez utiliser NuGet pour l'installer. Exécutez la commande suivante dans le Gestionnaire De Paquets De La Console .

PM> Install-Package TinyCsvParser

Utilisation

Imaginez que nous ayons une liste de personnes dans un fichier CSV persons.csv avec leur prénom, nom de famille 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 la correspondance entre les colonnes dans les données CSV et la propriété dans vous du 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 ensuite nous pouvons utiliser la cartographie pour analyser 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<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);
        }
    }
}

Guide De L'Utilisateur

un guide D'utilisation complet est disponible à l'adresse suivante:

7
répondu bytefish 2016-01-31 10:19:02

il n'y a aucun moyen officiel que je connaisse, mais vous devriez en effet utiliser les bibliothèques existantes. Voici un que j'ai trouvé très utile de Codeprojet:

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

3
répondu VitalyB 2010-01-17 15:24:08

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

}
1
répondu Alex Begun 2016-12-20 20:27:57

il y a quelque temps j'avais écrit classe simple pour CSV lire/écrire basé sur Microsoft.VisualBasic bibliothèque. En utilisant cette classe simple, vous serez en mesure de travailler avec CSV comme avec 2 dimensions array. Vous pouvez trouver ma classe par le lien suivant: https://github.com/ukushu/DataExporter

exemple Simple d'usage:

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 la lecture de l'en-tête que vous devez est de lire csv.Rows[0] cellules":)

1
répondu Andrew 2017-04-01 04:17:49

fichier source Unique solution pour la simple analyse des besoins, utile. Il s'occupe de tous les cas de bordereaux. Comme la normalisation de la nouvelle ligne et la manipulation de nouvelles lignes dans les littérales de corde citées. Votre accueil!

si votre fichier CSV a un en-tête, vous n'avez qu'à lire les noms des colonnes (et à calculer les index des colonnes) à partir de la première ligne. Simple que cela.

notez que Dump est une méthode LINQPad, vous pourriez vouloir supprimer que 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;
    }
}
1
répondu John Leidegren 2018-01-20 13:52:40

basé sur l'article de unlimit sur comment bien diviser un CSV en utilisant la fonction C# split ()? :

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

NOTE: Ceci ne gère pas les virgules évadées / imbriquées, etc., et ne convient donc qu'à certaines listes CSV simples.

0
répondu radsdau 2017-05-23 10:31:37

ce code se 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;
}
0
répondu PolinaC 2017-06-16 19:07:54

un autre à cette liste, Cinchoo ETL - une bibliothèque open source pour lire et écrire des formats de fichiers multiples (CSV, flat file, Xml, JSON etc)

exemple ci-dessous montre comment lire le fichier CSV rapidement (aucun objet POCO requis)

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

exemple ci-dessous montre comment lire un fichier CSV en utilisant l'objet POCO

public partial class EmployeeRec
{
    public int Id { get; set; }
    public string 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<EmployeeRec>(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);
        }
    }
}

veuillez vérifier les articles à CodeProject sur la façon de utiliser.

0
répondu RajN 2018-01-06 16:18:39