c# - Importations de fichiers CSV dans .Net

Translate

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

Quelle est la meilleure façon d'importer un fichier CSV dans une structure de données fortement typée? Encore une fois simple = mieux.

This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

MicrosoftTextFieldParserest stable et suitRFC 4180pour les fichiers CSV. Ne soyez pas rebuté par leMicrosoft.VisualBasicespace de noms; c'est un composant standard dans le .NET Framework, il suffit d'ajouter une référence au globalMicrosoft.VisualBasicAssemblée.

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

Voir également:Guide pratique pour lire des fichiers texte délimités par des virgules dans Visual Basicpour un exemple de code VB.

La source
Translate

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();
La source
ICR
Translate

Si vous vous attendez à des scénarios assez complexes pour l'analyse CSV,ne pense même pas à lancer notre propre analyseur. Il existe de nombreux excellents outils, commeFileHelpers, ou même ceux deCodeProject.

Le fait est que c'est un problème assez courant et vous pouvez parier quebeaucoupdes développeurs de logiciels ont déjà réfléchi et résolu ce problème.

La source
Translate

Brian donne une belle solution pour le convertir en une collection fortement typée.

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

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

    StringReader stringReader = new StringReader(content);

    bool inQoutedString = false;
    IList<string> record = new List<string>();
    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<string>();

                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 cela ne gère pas la casse des champs non délimités par des guillemets doubles, mais meerley ayant une chaîne entre guillemets à l'intérieur. Voirce postpour un peu plus de développement ainsi que des liens vers des bibliothèques appropriées.

La source
Translate

Je suis d'accord avec @Pas moi. FileHelpersest bien testé et gère toutes sortes de cas extrêmes que vous devrez éventuellement gérer si vous le faites vous-même. Jetez un œil à ce que fait FileHelpers et n'écrivez les vôtres que si vous êtes absolument sûr que (1) vous n'aurez jamais besoin de gérer les cas extrêmes que FileHelpers fait, ou (2) vous aimez écrire ce genre de choses et allez soyez ravi lorsque vous devez analyser des choses comme celle-ci:

1, "Bill", "Smith", "Supervisor", "No Comment"

2, "Drake", "O'Malley", "Concierge,

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

La source
Translate

Je m'ennuyais donc j'ai modifié certaines choses que j'ai écrites. Il essaie d'encapsuler l'analyse d'une manière OO tout en réduisant le nombre d'itérations dans le fichier, il n'itère qu'une fois en haut pour chacun.

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<Person> persons = new List<Person>();
            // 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<String[]> 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<Person> GetPersons()
        {
            foreach (String[] record in this.Parser.GetRecords())
            {
                yield return new Person()
                {
                    Name = record[0],
                    Address = record[1],
                    DOB = DateTime.Parse(record[2]),
                };
            }
        }
    }
}
La source
Translate

Il existe deux articles sur CodeProject qui fournissent du code pour une solution, un qui utiliseStreamReaderet un quiimporte des données CSVen utilisant lePilote de texte Microsoft.

La source
Translate

Un bon moyen 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. Soyez prudent lorsque vous manipulez la première ligne.

C'est peut-être au-dessus de votre tête, mais il semble y avoir un moyen direct d'y accéder également en utilisant unchaî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 gros du travail pour vous.

La source
Translate

J'ai dû utiliser un analyseur CSV en .NET pour un projet cet été et me suis installé sur le pilote Microsoft Jet Text. 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 ne l'ai pas fait au début, mais ensuite j'obtenais 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 colonnes au-dessus de 64 caractères; il tronque. Cela ne devrait pas être un 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 me méfierais de lancer mon propre analyseur CSV, car je manquerais probablement certains des cas de fin, et je n'ai trouvé aucun autre package d'analyse CSV gratuit pour .NET.

EDIT: De plus, il ne peut y avoir qu'un seul fichier schema.ini par répertoire, je l'ai donc ajouté dynamiquement pour taper fortement les colonnes nécessaires. Il ne tapera fortement que les colonnes spécifiées et en déduira pour tout champ non spécifié. J'ai vraiment apprécié cela, car je travaillais à importer un fichier CSV fluide de plus de 70 colonnes et je ne voulais pas spécifier chaque colonne, uniquement celles qui se comportaient mal.

La source
Translate

J'ai tapé du code. Le résultat dans le datagridviewer semblait bon. Il analyse une seule ligne de texte en 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;
    }
La source
Translate

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

Par exemple:

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

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

La source