Aquí podría ser tu PUBLICIDAD


Importaciones de archivos CSV en .Net

votos
99

Me doy cuenta de que esta es una pregunta para principiantes, pero estoy buscando una solución simple, parece que debería haber una.

¿Cuál es la mejor manera de importar un archivo CSV en una estructura de datos fuertemente tipada? Otra vez simple = mejor.

Publicado el 05/08/2008 a las 05:43
fuente por usuario MattH
En otros idiomas...        العربية       

12 respuestas

votos
72

TextFieldParser de Microsoft es estable y sigue RFC 4180 para archivos CSV. No te dejes desanimar por el Microsoft.VisualBasicespacio de nombres; es un componente estándar en .NET Framework, solo agrega una referencia al Microsoft.VisualBasicensamblaje global .

Si está compilando para Windows (en lugar de Mono) y no prevé tener que analizar archivos CSV "rotos" (que no son compatibles con RFC), esta sería la opción obvia, ya que es gratis, sin restricciones, estable, y soportado activamente, la mayoría de los cuales no se puede decir de FileHelpers.

Consulte también: Cómo leer archivos de texto delimitados por comas en Visual Basic para obtener un ejemplo de código VB.

Respondida el 01/04/2009 a las 08:58
fuente por usuario MarkJ


Aquí podría ser tu PUBLICIDAD


votos
48

Consulte FileHelpers Open Source Library .

Respondida el 05/08/2008 a las 05:47
fuente por usuario NotMyself

votos
21

Use una conexión 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();
Respondida el 05/11/2008 a las 03:41
fuente por usuario Kevin

votos
12

Si está esperando escenarios bastante complejos para el análisis de CSV, no piense en rodar nuestro propio analizador . Hay muchas herramientas excelentes, como FileHelpers , o incluso de CodeProject .

El punto es que este es un problema bastante común y podría apostar a que muchos desarrolladores de software ya han pensado y resuelto este problema.

Respondida el 17/08/2008 a las 12:44
fuente por usuario Jon Limjap

votos
9

Estoy de acuerdo con @ NotMyself . FileHelpers está bien probado y maneja todo tipo de casos extremos con los que eventualmente tendrás que lidiar si lo haces tú mismo. Eche un vistazo a lo que FileHelpers hace y solo escriba el suyo si está absolutamente seguro de que (1) nunca necesitará manejar los casos extremos que FileHelpers tiene, o (2) le encanta escribir este tipo de cosas y van a alégrate cuando tengas que analizar cosas como esta:

1, "Bill", "Smith", "Supervisor", "Sin comentarios"

2, 'Drake', 'O'Malley', "Conserje,

Vaya, no estoy citado y estoy en una nueva línea!

Respondida el 17/08/2008 a las 12:53
fuente por usuario Jon Galloway

votos
9

Brian ofrece una buena solución para convertirla en una colección fuertemente tipada.

La mayoría de los métodos de análisis CSV dados no tienen en cuenta los campos de escape o algunas de las otras sutilezas de los archivos CSV (como el recorte de campos). Aquí está el código que uso personalmente. Es un poco rudo en los bordes y prácticamente no reporta errores.

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

Tenga en cuenta que esto no maneja el caso del borde de los campos que no están delimitados por comillas dobles, pero meerley tiene una cadena de citas dentro de él. Vea esta publicación para una mejor expansión así como algunos enlaces a algunas bibliotecas apropiadas.

Respondida el 08/08/2008 a las 05:20
fuente por usuario ICR

votos
6

Estaba aburrido, así que modifiqué algunas cosas que escribí. Intenta encapsular el análisis sintáctico de una manera OO, al reducir la cantidad de iteraciones a través del archivo, solo itera una vez en la parte superior de todos.

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]),
                };
            }
        }
    }
}
Respondida el 08/08/2008 a las 10:39
fuente por usuario Brian Leahy

votos
5

Hay dos artículos en CodeProject que proporcionan código para una solución, uno que usa StreamReader y otro que importa datos CSV usando el Microsoft Text Driver .

Respondida el 05/08/2008 a las 06:24
fuente por usuario Yaakov Ellis

votos
2

Una buena manera simple de hacerlo es abrir el archivo y leer cada línea en una matriz, lista vinculada, estructura de datos de tu elección. Sin embargo, tenga cuidado con el manejo de la primera línea.

Esto puede pasar por alto, pero parece que hay una forma directa de acceder a ellos también utilizando una cadena de conexión .

¿Por qué no intentar usar Python en lugar de C # o VB? Tiene un buen módulo CSV para importar que hace todo el trabajo pesado por ti.

Respondida el 05/08/2008 a las 05:49
fuente por usuario helloandre

votos
1

He escrito en algún código. El resultado en el datagridviewer se veía bien. Se analiza una sola línea de texto a un ArrayList de objetos.

    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;
    }
Respondida el 09/09/2011 a las 11:02
fuente por usuario Pieter

votos
1

Tuve que usar un analizador CSV en .NET para un proyecto este verano y decidí utilizar el Microsoft Jet Text Driver. Usted especifica una carpeta usando una cadena de conexión, luego consulta un archivo usando una declaración SQL Select. Puede especificar tipos fuertes utilizando un archivo schema.ini. No lo hice al principio, pero luego recibí malos resultados en los que el tipo de datos no era aparente de inmediato, como los números de IP o una entrada como "XYQ 3.9 SP1".

Una de las limitaciones con las que me encontré es que no puede manejar nombres de columnas de más de 64 caracteres; trunca Esto no debería ser un problema, excepto que estaba tratando con datos de entrada muy mal diseñados. Devuelve un conjunto de datos ADO.NET.

Esta fue la mejor solución que encontré. Sería cauteloso de rodar mi propio analizador CSV, ya que probablemente me perdería algunos de los casos finales, y no encontré ningún otro paquete de análisis CSV gratuito para .NET.

EDITAR: Además, solo puede haber un archivo schema.ini por directorio, así que lo agregué dinámicamente para escribir con fuerza las columnas necesarias. Solo escribirá con fuerza las columnas especificadas e inferirá para cualquier campo no especificado. Realmente lo aprecié, ya que estaba tratando de importar un fluido CSV de columna de más de 70 y no quería especificar cada columna, solo las que se comportaban mal.

Respondida el 16/08/2008 a las 11:15
fuente por usuario pbh101

votos
0

Si puede garantizar que no hay comas en los datos, la forma más simple probablemente sea usar String.split .

Por ejemplo:

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

Puede haber bibliotecas que pueda usar para ayudar, pero eso es probablemente lo más simple que pueda obtener. Solo asegúrate de no tener comas en los datos, de lo contrario necesitarás analizarlos mejor.

Respondida el 05/08/2008 a las 06:02
fuente por usuario Mike Stone


Aquí podría ser tu PUBLICIDAD