Importations de fichiers CSV dans .Net

Je me rends compte que c'est une question de débutant, mais je cherche une solution simple - il semble qu'il devrait y en avoir une.

Quel est le meilleur moyen d'importer un fichier CSV dans une structure de données fortement typée? Encore simple = mieux.

0
Considérant que cela a été créé un an plus tôt que 1103495, je pense que cette question est une copie de celui-ci.
ajouté l'auteur MattH, source
ajouté l'auteur Mark Meuer, source
Merci, Matt. J'essayais juste de les lier ensemble, pas d'indiquer lequel venait en premier. Vous verrez que j'ai exactement le même texte sur l'autre question pointant vers celui-ci. Y a-t-il une meilleure façon de lier deux questions ensemble?
ajouté l'auteur Mark Meuer, source

12 Réponses

Si vous pouvez garantir qu'il n'y a pas de virgules dans les données, le plus simple serait probablement d'utiliser String.split .

Par exemple:

String[] values = myString.Split(',');
myObject.StringField = values[0];
myObject.IntField = Int32.Parse(values[1]);

Il peut y avoir des bibliothèques que vous pourriez utiliser pour vous aider, mais c'est probablement aussi simple que possible. Assurez-vous juste que vous ne pouvez pas avoir de virgules dans les données, sinon vous aurez besoin de mieux analyser.

0
ajouté
très mauvais sur l'utilisation de la mémoire et beaucoup de frais généraux. Petit devrait être moins merci quelques kilo-octets. Certainement pas bon pour un csv 10mb!
ajouté l'auteur ppumkin, source
ce n'est pas une solution optimale
ajouté l'auteur roundcrisis, source
Cela dépend de la taille de votre mémoire et du fichier.
ajouté l'auteur tonymiao, source

Brian donne une bonne solution pour la convertir en une collection fortement typée.

La plupart des méthodes d'analyse CSV données ne tiennent pas compte des champs d'échappement ou de certaines autres subtilités des fichiers CSV (comme les champs de rognage). Voici le code que j'utilise personnellement. C'est un peu rude sur les bords et a pratiquement aucun rapport d'erreur.

public static IList> Parse(string content)
{
    IList> records = new List>();

    StringReader stringReader = new StringReader(content);

    bool inQoutedString = false;
    IList record = new List();
    StringBuilder fieldBuilder = new StringBuilder();
    while (stringReader.Peek() != -1)
    {
        char readChar = (char)stringReader.Read();

        if (readChar == '\n' || (readChar == '\r' && stringReader.Peek() == '\n'))
        {
            // If it's a \r\n combo consume the \n part and throw it away.
            if (readChar == '\r')
            {
                stringReader.Read();
            }

            if (inQoutedString)
            {
                if (readChar == '\r')
                {
                    fieldBuilder.Append('\r');
                }
                fieldBuilder.Append('\n');
            }
            else
            {
                record.Add(fieldBuilder.ToString().TrimEnd());
                fieldBuilder = new StringBuilder();

                records.Add(record);
                record = new List();

                inQoutedString = false;
            }
        }
        else if (fieldBuilder.Length == 0 && !inQoutedString)
        {
            if (char.IsWhiteSpace(readChar))
            {
                // Ignore leading whitespace
            }
            else if (readChar == '"')
            {
                inQoutedString = true;
            }
            else if (readChar == ',')
            {
                record.Add(fieldBuilder.ToString().TrimEnd());
                fieldBuilder = new StringBuilder();
            }
            else
            {
                fieldBuilder.Append(readChar);
            }
        }
        else if (readChar == ',')
        {
            if (inQoutedString)
            {
                fieldBuilder.Append(',');
            }
            else
            {
                record.Add(fieldBuilder.ToString().TrimEnd());
                fieldBuilder = new StringBuilder();
            }
        }
        else if (readChar == '"')
        {
            if (inQoutedString)
            {
                if (stringReader.Peek() == '"')
                {
                    stringReader.Read();
                    fieldBuilder.Append('"');
                }
                else
                {
                    inQoutedString = false;
                }
            }
            else
            {
                fieldBuilder.Append(readChar);
            }
        }
        else
        {
            fieldBuilder.Append(readChar);
        }
    }
    record.Add(fieldBuilder.ToString().TrimEnd());
    records.Add(record);

    return records;
}

Notez que ceci ne gère pas le cas de bord des champs n'étant pas éliminés par des guillemets, mais meerley ayant une chaîne entre guillemets. Voir cet article pour un peu d'une meilleure expansion ainsi que des liens vers quelques bonnes bibliothèques.

0
ajouté

Il existe deux articles sur CodeProject qui fournissent du code pour une solution, un qui utilise StreamReader et celui qui importe les données CSV en utilisant le Pilote de texte Microsoft .

0
ajouté

Si vous attendez des scénarios assez complexes pour l'analyse CSV, ne pensez même pas à lancer notre propre analyseur . Il y a beaucoup d'excellents outils, comme FileHelpers , ou même ceux de CodeProject .

Le fait est que c'est un problème assez commun et vous pourriez parier que beaucoup de développeurs de logiciels ont déjà pensé et résolu ce problème.

0
ajouté
Merci @techspider J'espère que vous avez noté que ce post était de la période bêta de StackOverflow: D Cela étant dit, de nos jours, les outils CSV sont mieux issus des paquets Nuget - donc je ne suis pas sûr que même les réponses de liens sont immunisées contre 8 ans cycles d'évolution de la technologie
ajouté l'auteur Jon Limjap, source
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses à lien uniquement peuvent devenir invalides si la page liée change. - De l'avis
ajouté l'auteur techspider, source
0
ajouté
+1 Juste mis en œuvre ce ... génial
ajouté l'auteur Miyagi Coder, source
@dangph Je ne pense pas que ce soit vrai. opensource.org/licenses/lgpl-2.1.php "Toutefois, les liens un "travail qui utilise la bibliothèque" avec la bibliothèque crée un exécutable qui est un dérivé de la bibliothèque ... L'exécutable est donc couvert par cette licence, la section 6 énonce les conditions de distribution de ces exécutables. "
ajouté l'auteur RYFN, source
@dangph Je suis d'accord, je ne sais pas trop quoi en faire!
ajouté l'auteur RYFN, source
@ John, pourquoi dites-vous cela? LGPL ne vous oblige pas à libérer du code, sauf si vous modifiez la bibliothèque elle-même. (Dans ce cas, il serait logique de soumettre un patch de toute façon.)
ajouté l'auteur dan-gph, source
@ Zeus, je ne pense toujours pas que vous devez libérer la source de votre "travail qui utilise la bibliothèque". Vous devez libérer "code objet et / ou code source". Je ne suis pas sûr de ce que cela signifie dans un environnement .Net. Mais tu as raison. Les exigences de la section 6 sont extrêmement lourdes. Quelle licence ridicule.
ajouté l'auteur dan-gph, source
Un autre problème avec FileHelpers est que le développement semble avoir complètement arrêté depuis 2007. Et malheureusement, il contient des bogues. (Cela fonctionnerait probablement bien pour les cas simples.) Même s'il est open source, il n'est pas certain que l'auteur accepte les correctifs.
ajouté l'auteur dan-gph, source
Malheureusement, c'est LGPL, ce qui est loin d'être idéal dans un environnement d'entreprise ...
ajouté l'auteur John Weldon, source
À titre de comparaison, NHibernate est également LGPL et il a été utilisé dans d'innombrables applications commerciales. Donc, il n'y a pas de quoi s'inquiéter.
ajouté l'auteur Mauricio Scheffer, source
@dangph @Zeus @John @Martin La section 6b indique que vous êtes autorisé à "utiliser un mécanisme de bibliothèque partagée approprié pour la liaison avec la bibliothèque." Un mécanisme approprié est celui qui (1) utilise au moment de l'exécution une copie de la bibliothèque déjà présente sur la bibliothèque. système informatique de l'utilisateur, plutôt que de copier les fonctions de la bibliothèque dans l'exécutable, et (2) fonctionnera correctement avec une version modifiée de la bibliothèque, si l'utilisateur en installe un, tant que la version modifiée est compatible avec la version que le
ajouté l'auteur MarkJ, source
Le fait que la légalité de ceci est assez compliqué pour provoquer cette discussion l'exclut probablement pour beaucoup de gens. Cela montre certainement que la situation est incertaine. La plupart des gens n'ont pas le temps ou l'argent pour faire venir «l'équipe de la ligue» pour vérifier la situation. Cela dit, à moins que vous n'alliez incorporer la librairie dans un bateau de solution emballé à des milliers de personnes, il est peu probable que quiconque fasse appliquer la licence de toute façon.
ajouté l'auteur Martin Brown, source
La page d'accueil de FileHelpers dit: "La bibliothèque de FileHelpers est @Copyright 2005-2006 à Marcos Meli mais c'est le code source et les binaires sont libres pour l'usage commercial et non commercial."
ajouté l'auteur Carl Hörberg, source

Une bonne façon simple de le faire est d'ouvrir le fichier et de lire chaque ligne dans un tableau, une liste chaînée, une structure de données de votre choix. Faites attention à la manipulation de la première ligne cependant.

Cela peut être sur votre tête, mais il semble y avoir un moyen direct d'y accéder en utilisant un chaîne de connexion .

Pourquoi ne pas essayer d'utiliser Python au lieu de C# ou VB? Il a un joli module CSV à importer qui fait tout le travail lourd pour vous.

0
ajouté
Ne sautez pas en Python de VB pour un analyseur CSV. Il y en a un en VB. Bien que bizarrement, il semble avoir été ignoré dans les réponses à cette question. msdn.microsoft.com/fr-fr/library/ & hellip;
ajouté l'auteur MarkJ, source

Je suis d'accord avec @ NotMyself . FileHelpers est bien testé et gère toutes sortes de cas de bords que vous aurez éventuellement à traiter si vous le faites c'est toi. Jetez un coup d'œil à ce que fait FileHelpers et n'écrivez que les vôtres si vous êtes absolument certain que (1) vous n'aurez jamais besoin de gérer les cas de bords que FileHelpers fait, ou (2) vous aimez écrire ce genre de choses et allez être fou de joie quand vous devez analyser des choses comme ceci:

1, "Bill", "Smith", "Superviseur", "Aucun commentaire"

2, «Drake», «O'Malley», «Concierge,

Oups, je ne suis pas cité et je suis sur une nouvelle ligne!

0
ajouté

Je m'ennuyais alors j'ai modifié certaines choses que j'ai écrites. Il essaye d'encapsuler l'analyse d'une manière OO tout en réduisant la quantité d'itérations à travers le fichier, il itère seulement une fois au sommet foreach.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO;

namespace ConsoleApplication1
{
    class Program
    {

        static void Main(string[] args)
        {

            // usage:

            // note this wont run as getting streams is not Implemented

            // but will get you started

            CSVFileParser fileParser = new CSVFileParser();

            // TO Do:  configure fileparser

            PersonParser personParser = new PersonParser(fileParser);

            List persons = new List();
            // if the file is large and there is a good way to limit
            // without having to reparse the whole file you can use a 
            // linq query if you desire
            foreach (Person person in personParser.GetPersons())
            {
                persons.Add(person);
            }

            // now we have a list of Person objects
        }
    }

    public abstract  class CSVParser 
    {

        protected String[] deliniators = { "," };

        protected internal IEnumerable GetRecords()
        {

            Stream stream = GetStream();
            StreamReader reader = new StreamReader(stream);

            String[] aRecord;
            while (!reader.EndOfStream)
            {
                  aRecord = reader.ReadLine().Split(deliniators,
                   StringSplitOptions.None);

                yield return aRecord;
            }

        }

        protected abstract Stream GetStream(); 

    }

    public class CSVFileParser : CSVParser
    {
        // to do: add logic to get a stream from a file

        protected override Stream GetStream()
        {
            throw new NotImplementedException();
        } 
    }

    public class CSVWebParser : CSVParser
    {
        // to do: add logic to get a stream from a web request

        protected override Stream GetStream()
        {
            throw new NotImplementedException();
        }
    }

    public class Person
    {
        public String Name { get; set; }
        public String Address { get; set; }
        public DateTime DOB { get; set; }
    }

    public class PersonParser 
    {

        public PersonParser(CSVParser parser)
        {
            this.Parser = parser;
        }

        public CSVParser Parser { get; set; }

        public  IEnumerable GetPersons()
        {
            foreach (String[] record in this.Parser.GetRecords())
            {
                yield return new Person()
                {
                    Name = record[0],
                    Address = record[1],
                    DOB = DateTime.Parse(record[2]),
                };
            }
        }
    }
}
0
ajouté

Utilisez une connexion OleDB.

String sConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\InputDirectory\\;Extended Properties='text;HDR=Yes;FMT=Delimited'";
OleDbConnection objConn = new OleDbConnection(sConnectionString);
objConn.Open();
DataTable dt = new DataTable();
OleDbCommand objCmdSelect = new OleDbCommand("SELECT * FROM file.csv", objConn);
OleDbDataAdapter objAdapter1 = new OleDbDataAdapter();
objAdapter1.SelectCommand = objCmdSelect;
objAdapter1.Fill(dt);
objConn.Close();
0
ajouté
Cela nécessite un accès au système de fichiers. Pour autant que je sache, il n'y a aucun moyen de faire fonctionner OLEDB avec des flux en mémoire :(
ajouté l'auteur UserControl, source
Je ne me plains pas. En fait, je préfèrerais la solution OLEDB au reste, mais j'étais frustré tant de fois quand il fallait analyser CSV dans des applications ASP.NET, donc je voulais le noter.
ajouté l'auteur UserControl, source
@UserControl, bien sûr, il nécessite un accès au système de fichiers. Il a demandé à propos de l'importation d'un fichier CSV
ajouté l'auteur Kevin, source

TextFieldParser de Microsoft est stable et suit RFC 4180 pour les fichiers CSV. Ne soyez pas rebutés par l'espace de noms Microsoft.VisualBasic ; C'est un composant standard dans le .NET Framework, il suffit d'ajouter une référence à l'assembly global Microsoft.VisualBasic .

Si vous compilez pour Windows (par opposition à Mono) et ne prévoyez pas d'analyser des fichiers CSV "cassés" (non conformes à la RFC), alors ce serait le choix évident, car il est libre, sans restriction, stable, et activement pris en charge, dont la plupart ne peut pas être dit pour FileHelpers.

Voir aussi: Comment: lire des fichiers texte délimités par des virgules dans Visual Basic pour un exemple de code VB.

0
ajouté
Le TextFieldParser fonctionnera aussi bien pour les images cryptées délimitées par des tabulations que pour les autres. Je me rends compte que votre réponse précédente ne prétendait pas que la bibliothèque était spécifique à la VB, cela m'a semblé impliquer que c'était vraiment pour signifié pour VB, et pas destiné à utiliser à partir de C#, ce que je ne pense pas être le cas - il existe des classes vraiment utiles dans MSVB.
ajouté l'auteur Aaronaught, source
Il n'y a rien de spécifique à VB à propos de cette classe autre que son namespace malheureusement nommé. Je choisirais certainement cette bibliothèque si j'avais seulement besoin d'un analyseur CSV "simple", car il n'y a rien à télécharger, à distribuer ou à s'inquiéter en général. À cette fin, j'ai édité le phrasé VB-axé sur cette réponse.
ajouté l'auteur Aaronaught, source
@Aaronaught Je pense que vos modifications sont pour la plupart une amélioration. Bien que cette RFC ne fasse pas nécessairement autorité, comme beaucoup d'auteurs CSV ne s'y conforment pas, par ex. Excel n'utilise pas toujours une virgule dans les fichiers "CSV". Aussi, ma réponse précédente ne disait-elle pas déjà que la classe pourrait être utilisée à partir de C #?
ajouté l'auteur MarkJ, source

J'ai dû utiliser un analyseur CSV dans .NET pour un projet cet été et je me suis installé sur le pilote de texte Microsoft Jet. Vous spécifiez un dossier à l'aide d'une chaîne de connexion, puis interrogez un fichier à l'aide d'une instruction SQL Select. Vous pouvez spécifier des types forts à l'aide d'un fichier schema.ini. Je n'ai pas fait cela au début, mais ensuite je recevais de mauvais résultats où le type de données n'était pas immédiatement apparent, comme les numéros IP ou une entrée comme "XYQ 3.9 SP1".

Une limitation que j'ai rencontrée est qu'il ne peut pas gérer les noms de colonne au-dessus de 64 caractères; il tronque. Cela ne devrait pas poser de problème, sauf que j'avais affaire à des données d'entrée très mal conçues. Il renvoie un DataSet ADO.NET.

C'était la meilleure solution que j'ai trouvée. Je serais méfiant de rouler mon propre analyseur CSV, car je manquerais probablement certains des cas de fin, et je n'ai pas trouvé d'autres paquets d'analyse CSV gratuits pour .NET là-bas.

EDIT: De plus, il ne peut y avoir qu'un fichier schema.ini par répertoire, donc je l'ai ajouté dynamiquement pour taper les colonnes nécessaires. Il ne tape que fortement les colonnes spécifiées et déduit pour tout champ non spécifié. J'ai vraiment apprécié ceci, car je m'occupais de l'importation d'un fichier CSV de 70+ colonnes et je ne voulais pas spécifier chaque colonne, seulement celles qui se conduisaient mal.

0
ajouté
Pourquoi pas le VB.NET construit en analyseur CSV? msdn.microsoft.com/fr-fr/library/ & hellip;
ajouté l'auteur MarkJ, source

J'ai tapé du code. Le résultat dans le datagridviewer a semblé bon. Il analyse une seule ligne de texte à une liste d'objets.

    enum quotestatus
    {
        none,
        firstquote,
        secondquote
    }
    public static System.Collections.ArrayList Parse(string line,string delimiter)
    {        
        System.Collections.ArrayList ar = new System.Collections.ArrayList();
        StringBuilder field = new StringBuilder();
        quotestatus status = quotestatus.none;
        foreach (char ch in line.ToCharArray())
        {                                
            string chOmsch = "char";
            if (ch == Convert.ToChar(delimiter))
            {
                if (status== quotestatus.firstquote)
                {
                    chOmsch = "char";
                }                         
                else
                {
                    chOmsch = "delimiter";                    
                }                    
            }

            if (ch == Convert.ToChar(34))
            {
                chOmsch = "quotes";           
                if (status == quotestatus.firstquote)
                {
                    status = quotestatus.secondquote;
                }
                if (status == quotestatus.none )
                {
                    status = quotestatus.firstquote;
                }
            }

            switch (chOmsch)
            {
                case "char":
                    field.Append(ch);
                    break;
                case "delimiter":                        
                    ar.Add(field.ToString());
                    field.Clear();
                    break;
                case "quotes":
                    if (status==quotestatus.firstquote)
                    {
                        field.Clear();                            
                    }
                    if (status== quotestatus.secondquote)
                    {                                                                           
                            status =quotestatus.none;                                
                    }                    
                    break;
            }
        }
        if (field.Length != 0)            
        {
            ar.Add(field.ToString());                
        }           
        return ar;
    }
0
ajouté