CSV Le importazioni di file in .Net

voti
99

Mi rendo conto che questa è una domanda newbie, ma sto cercando una soluzione semplice - sembra che ci dovrebbe essere uno.

Qual è il modo migliore per importare un file CSV in una struttura di dati fortemente tipizzato? Ulteriore semplici = migliori.

È pubblicato 05/08/2008 alle 05:43
fonte dall'utente
In altre lingue...                            


12 risposte

voti
72

Microsoft TextFieldParser è stabile e segue RFC 4180 per i file CSV. Non lasciatevi scoraggiare dal Microsoft.VisualBasicnamespace; si tratta di un componente standard in .NET Framework, è sufficiente aggiungere un riferimento globale Microsoft.VisualBasicdi montaggio.

Se si compila per Windows (al contrario di Mono) e non prevedono di dover analizzare "rotto" i file CSV (non RFC-compliant), allora questo sarebbe la scelta più ovvia, in quanto è gratuito, senza restrizioni, stabile, e attivamente sostenuto, la maggior parte dei quali non si può dire per FileHelpers.

Vedere anche: Procedura: leggere da file di testo delimitato da virgola in Visual Basic per un esempio di codice VB.

Risposto il 01/04/2009 a 20:58
fonte dall'utente

Risposto il 05/08/2008 a 05:47
fonte dall'utente

voti
21

Utilizzare una connessione 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();
Risposto il 05/11/2008 a 15:41
fonte dall'utente

voti
12

Se vi aspettate scenari piuttosto complesse per l'analisi CSV, non pensate nemmeno di rotolamento nostro parser . Ci sono un sacco di ottimi strumenti là fuori, come FileHelpers , o anche quelli di CodeProject .

Il punto è questo è un problema abbastanza comune e si potrebbe scommettere che un sacco di sviluppatori di software hanno già pensato e risolto questo problema.

Risposto il 17/08/2008 a 00:44
fonte dall'utente

voti
9

Sono d'accordo con @ NotMyself . FileHelpers è ben collaudato e gestisce tutti i tipi di casi limite che più volte si hanno a che fare con se lo fai da te. Date un'occhiata a ciò che FileHelpers fa e solo scrivere il proprio se si è assolutamente sicuri che sia (1) non avrai mai bisogno per gestire i casi limite FileHelpers fa, o (2) ami scrivere questo genere di cose e stanno andando a essere felice quando si deve analizzare cose come questa:

1, "Bill", "Smith", "Supervisore", "No Comment"

2, 'Drake,', 'O'Malley', "Portinaio,

Ops, non sto citato e io sono su una nuova linea!

Risposto il 17/08/2008 a 00:53
fonte dall'utente

voti
9

Brian dà una bella soluzione per convertirlo in un insieme fortemente tipizzato.

La maggior parte dei metodi di analisi CSV dati non prendono in considerazione i campi di fuga o alcune delle altre sottigliezze di file CSV (come i campi di taglio). Qui è il codice che personalmente uso. E 'un po' ruvido intorno ai bordi e ha praticamente alcuna segnalazione degli errori.

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

Si noti che questo non gestisce il caso limite dei campi non essere deliminated tra virgolette, ma meerley avendo una stringa tra virgolette all'interno di esso. Vedi questo post per un po 'di un expanation meglio così come alcuni link ad alcune librerie appropriate.

Risposto il 08/08/2008 a 17:20
fonte dall'utente

voti
6

Mi annoiavo così ho modificato alcune cose che ho scritto. E 'cercare di incapsulare il parsing in modo OO whle riducendo la quantità di iterazioni attraverso il file, esso soltanto itera una volta in cima 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<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]),
                };
            }
        }
    }
}
Risposto il 08/08/2008 a 10:39
fonte dall'utente

voti
5

Ci sono due articoli su CodeProject che forniscono il codice per una soluzione, quella che utilizza StreamReader e uno che importa i dati CSV utilizzando il driver del testo di Microsoft .

Risposto il 05/08/2008 a 06:24
fonte dall'utente

voti
2

Un buon modo semplice per farlo è quello di aprire il file, e leggere ogni riga in un array, lista collegata, i dati-struttura-del-tuo-scelta. Fate attenzione a gestire la prima linea, però.

Questo può essere sopra la vostra testa, ma sembra che ci sia un modo diretto per accedervi, nonché utilizzando una stringa di connessione .

Perché non provare a utilizzare Python invece di C # o VB? Ha una bella modulo di CSV per importazione che fa tutto il lavoro pesante per voi.

Risposto il 05/08/2008 a 05:49
fonte dall'utente

voti
1

Ho digitato un codice. Il risultato nel datagridviewer sembrava buono. Si analizza una sola riga di testo a un ArrayList di oggetti.

    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;
    }
Risposto il 09/09/2011 a 11:02
fonte dall'utente

voti
1

Ho dovuto usare un parser CSV in .NET per un progetto di questa estate e optato per il driver del testo di Microsoft Jet. È possibile specificare una cartella utilizzando una stringa di connessione, quindi eseguire una query un file utilizzando un'istruzione SQL SELECT. È possibile specificare i tipi di forti utilizzando un file Schema.ini. Non ho fatto questo in un primo momento, ma poi mi è stato sempre cattivi risultati in cui il tipo di dati non è stato immediatamente evidente, come i numeri IP o una voce del tipo "XYQ 3.9 SP1".

Una limitazione ho incontrato è che non può gestire i nomi di colonna al di sopra di 64 caratteri; tronca. Questo non dovrebbe essere un problema, tranne che avevo a che fare con dati di input molto mal progettati. Esso restituisce un DataSet di ADO.NET.

Questa è stata la soluzione migliore che ho trovato. Vorrei diffidare di rotazione mia parser CSV, dal momento che probabilmente perdere alcuni dei casi terminali, e non ho trovato alcun altri pacchetti di analisi gratuito CSV NET là fuori.

EDIT: Inoltre, non ci può essere un solo file schema.ini per directory, così ho dinamicamente aggiunto ad esso per digitare con forza le colonne necessarie. Sarà solo fortemente digitare le colonne specificate, e dedurre per qualsiasi campo specificato. Ho davvero apprezzato questo, come avevo a che fare con l'importazione di un fluido 70 + colonna CSV e non volevo specificare ogni colonna, solo quelli comportamento anomalo.

Risposto il 16/08/2008 a 23:15
fonte dall'utente

voti
0

Se si può garantire che non ci sono le virgole nei dati, quindi il modo più semplice sarebbe probabilmente quella di utilizzare String.split .

Per esempio:

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

Ci possono essere le biblioteche è possibile utilizzare per aiutare, ma che probabilmente è così semplice come si può ottenere. Basta fare in modo che non potete avere le virgole nei dati, in caso contrario è necessario analizzarlo meglio.

Risposto il 05/08/2008 a 06:02
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more