Vă mulțumim pentru susținere

Importarea fișierelor CSV în .Net

Îmi dau seama că aceasta este o întrebare de tip newbie, dar căut o soluție simplă - se pare că ar trebui să fie una.

Care este cel mai bun mod de a importa un fișier CSV într-o structură de date puternic tipată? Din nou simplu = mai bine.

0
adăugat editat
Având în vedere că acest lucru a fost creat cu un an mai devreme decât 1103495, cred că această întrebare este un duplicat al acestuia.
adăugat autor MattH
adăugat autor KMån
Acesta este un duplicat al stackoverflow.com/questions/1103495/…
adăugat autor Mark Meuer
Mulțumesc, Matt. Încercam doar să le legăm împreună, să nu menționăm care dintre ele a venit mai întâi. Veți vedea că am exact același text cu privire la cealaltă întrebare care indică acest lucru. Există o modalitate mai bună de a lega două întrebări împreună?
adăugat autor Mark Meuer

12 răspunsuri

Dacă puteți garanta că nu există virgule în date, atunci cea mai simplă cale ar fi probabil să utilizați String.split .

De exemplu:

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

Poate că există biblioteci pe care le puteți folosi pentru a vă ajuta, dar este, probabil, la fel de simplu cum puteți obține. Doar asigurați-vă că nu puteți avea virgule în date, altfel va trebui să o parsezi mai bine.

0
adăugat
foarte rău în utilizarea memoriei și o mulțime de cheltuieli generale. Mici ar trebui să fie mai puțin mulțumesc câteva kilobytes. Cu siguranta nu este bine pentru un csv de 10MB!
adăugat autor ppumkin
aceasta nu este o soluție optimă
adăugat autor roundcrisis
Depinde de dimensiunea memoriei și a fișierului.
adăugat autor tonymiao

Brian oferă o soluție frumoasă pentru ao transforma într-o colecție puternic tipărită.

Cele mai multe dintre metodele de analiză CSV date nu iau în considerare câmpurile care scapă sau unele dintre celelalte subtilități ale fișierelor CSV (cum ar fi câmpurile de decupare). Iată codul pe care îl folosesc personal. Este un pic cam dur in jurul margini si nu are nici un raport de eroare.

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

Rețineți că acest lucru nu se ocupă de câmpul de margine al câmpurilor care nu sunt delimitate prin ghilimele duble, dar meerley având un șir cotat în interiorul acestuia. Consultați această postare pentru o mai bună extindere, precum și câteva linkuri către unele biblioteci adecvate.

0
adăugat

Există două articole despre CodeProject care oferă cod pentru o soluție, una care folosește StreamReader și unul care importează date CSV utilizând Driver text Microsoft .

0
adăugat

Dacă așteptați scenarii destul de complexe pentru parsarea CSV, nici măcar nu vă gândiți la rularea propriului parser . Există o mulțime de instrumente excelente acolo, cum ar fi FileHelpers sau chiar de la CodeProject .

Ideea este că aceasta este o problemă destul de frecventă și ați putea să pariați că o mulțime de dezvoltatori de software s-au gândit deja și au rezolvat această problemă.

0
adăugat
Multumesc @techspider Sper ca ati observat ca acest post a fost din perioada beta a StackOverflow: D Acestea fiind spuse in zilele noastre, instrumentele CSV sunt mai bune din pachetele Nuget - deci nu sunt sigur daca raspunsurile la link-ul chiar sunt imune de la 8 ani - cicluri de evoluție ale tehnologiei
adăugat autor Jon Limjap
În timp ce această legătură poate răspunde la întrebare, este mai bine să includeți aici părțile esențiale ale răspunsului și să furnizați linkul pentru referință. Răspunsurile numai în legătură cu rapoartele pot deveni nevalabile dacă se modifică pagina legată. - Din examinare
adăugat autor techspider
0
adăugat
+1 Doar implementat acest ... minunat
adăugat autor Miyagi Coder
@dangph Nu cred că este adevărat. opensource.org/licenses/lgpl-2.1.php declară "Cu toate acestea, link-ul o "lucrare care folosește Biblioteca" cu Biblioteca creează un executabil care este un derivat al Bibliotecii ... Executivul este, prin urmare, acoperit de această Licență. Secțiunea 6 stabilește termenii pentru distribuirea unor astfel de executabile. "
adăugat autor RYFN
@dangph Sunt de acord, nu sunt sigur ce să fac din ea!
adăugat autor RYFN
@ John, de ce spui asta? LGPL nu solicită eliberarea vreunui cod dacă nu modificați chiar biblioteca. (În acest caz, ar fi logic să trimiteți oricum un plasture).
adăugat autor dan-gph
@ ZEUS, încă nu cred că trebuie să eliberați sursa "lucrării care folosește Biblioteca". Trebuie să eliberați "codul obiect și / sau codul sursă". Nu sunt sigur ce înseamnă asta într-un mediu .Net. Dar ai dreptate. Cerințele din secțiunea 6 sunt extrem de oneroase. Ce licență ridicolă.
adăugat autor dan-gph
O altă problemă cu FileHelpers este că dezvoltarea pe ea pare să fi fost complet blocată încă din 2007. Și, din păcate, conține bug-uri. (Probabil că ar funcționa bine pentru cazuri simple). Chiar dacă este o sursă deschisă, nu este clar că autorul acceptă patch-uri.
adăugat autor dan-gph
Din păcate, aceasta este LGPL, care este mai puțin decât ideală într-un mediu corporativ ...
adăugat autor John Weldon
Pentru comparație, NHibernate este, de asemenea, LGPL și a fost utilizat în nenumărate aplicații comerciale. Deci nu este nimic de îngrijorat.
adăugat autor Mauricio Scheffer
@dangph @Zeus @John @Martin Secțiunea 6b spune că vi se permite să "folosească un mecanism adecvat de bibliotecă partajat pentru a face legătura cu biblioteca. Un mecanism adecvat este acela care (1) folosește la momentul executării o copie a bibliotecii deja prezente pe bibliotecă sistemul de calcul al utilizatorului, mai degrabă decât copierea funcțiilor bibliotecii în executabil, și (2) va funcționa corect cu o versiune modificată a bibliotecii, dacă utilizatorul instalează unul, atâta timp cât versiunea modificată este compatibilă cu interfața cu versiunea pe care lucrarea a fost făcut cu.
adăugat autor MarkJ
Faptul că legalitatea acestui fapt este destul de complicată pentru a provoca această discuție probabil o exclude pentru o mulțime de oameni. Cu siguranță, situația este incertă. Majoritatea oamenilor nu au timp sau bani pentru a obține "echipa leagală" pentru a verifica situația. Acestea fiind spuse, dacă nu intenționați să includeți biblioteca într-o soluție de ambalare a navei la 1000 de oameni, este puțin probabil că oricine va aplica oricând licența.
adăugat autor Martin Brown
FileHelpers homepage spune: "Biblioteca FileHelpers este @Copyright 2005-2006 lui Marcos Meli, dar este codul sursă și binarele sunt gratuite pentru uz comercial și non-comercial."
adăugat autor Carl Hörberg

O modalitate foarte bună de a face acest lucru este deschiderea fișierului și citirea fiecărei linii într-o listă matricolă, asociată, structura de date a opțiunii dvs. Fiți atenți la manipularea primei linii deși.

Acest lucru poate fi peste capul dvs., dar se pare că există o modalitate directă de a le accesa, de asemenea, folosind un șir de conectare .

De ce nu încercați să utilizați Python în loc de C # sau VB? Acesta are un modul CSV frumos de importat, care face toată ridicarea grele pentru tine.

0
adăugat
Nu sari la python de la VB de dragul unui parser CSV. Există unul în VB. Deși pare ciudat că a fost ignorat în răspunsurile la această întrebare. msdn.microsoft.com/en-us/library/ & hellip;
adăugat autor MarkJ

Sunt de acord cu @ NotMyself . FileHelpers este bine testat și se ocupă de toate tipurile de cazuri de margine pe care în cele din urmă va trebui să le rezolvați dacă faceți chiar tu. Aruncați o privire la ceea ce face FileHelpers și scrieți-vă numai dacă sunteți absolut siguri că (1) nu va trebui niciodată să se ocupe de cazurile pe care FileHelpers le are, sau (2) vă place să scrieți astfel de lucruri și care urmează să fi bucuros când trebuie să analizați lucruri de genul:

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

2, "Drake", "O'Malley", "Janitor,

Oops, nu sunt citat și sunt pe o linie nouă!

0
adăugat

Am fost plictisit, așa că am modificat câteva lucruri pe care le-am scris. Se încearcă să încapsuleze parsarea într-o manieră OO, prin reducerea cantității de iterații prin fișier, ci doar iterează o singură dată în partea de sus a foreach-ului.

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
adăugat

Utilizați o conexiune 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
adăugat
Acest lucru necesită acces la sistemul de fișiere. Din câte știu că nu există nici o modalitate de a face OLEDB să lucreze cu fluxuri în memorie :(
adăugat autor UserControl
Nu mă plâng. De fapt, aș prefera soluția OLEDB peste restul, dar am fost frustrat de atâtea ori când a fost necesar să analizez CSV-ul în aplicațiile ASP.NET, așa că a vrut să-l noteze.
adăugat autor UserControl
@UserControl, desigur, necesită acces la sistemul de fișiere. El a întrebat despre importarea unui fișier CSV
adăugat autor Kevin

TextFieldParser este stabilă și urmează RFC 4180 pentru fișierele CSV. Nu fi oprit de spațiul de nume Microsoft.VisualBasic ; este o componentă standard în .NET Framework, trebuie doar să adăugați o referință la globală Microsoft.VisualBasic de asamblare.

Dacă compilați pentru Windows (spre deosebire de Mono) și nu anticipați că trebuie să parsezi fișiere CSV "rupte" (non-RFC-compliant), atunci aceasta ar fi alegerea evidentă, deoarece este liberă, nerestricționată, și sprijinit în mod activ, cele mai multe dintre acestea nu pot fi spuse pentru FileHelpers.

Consultați de asemenea: Cum să: citiți din fișierele text delimitate în comenzi în Visual Basic pentru un exemplu de cod VB.

0
adăugat
Textul TextFieldParser va funcționa, de asemenea, pentru tabelele delimitate de tabel și pentru alte structuri ciudate generate de Excel. Îmi dau seama că răspunsul tău anterior nu se pretindea că biblioteca era specifică de VB, tocmai mi-a venit-o înțeles că a fost într-adevăr însemnată pentru VB și nu intenționată să fie folosit din C #, ceea ce nu cred că este cazul - există câteva clase cu adevărat utile în MSVB.
adăugat autor Aaronaught
De fapt, nu există nimic specific VB despre această clasă, altul decât spațiul de nume numit din păcate. Aș alege cu siguranță această bibliotecă dacă am nevoie doar de un parser simplu "CSV", deoarece nu există nimic de descărcat, distribuit sau îngrijorat în general. În acest scop, am redactat expresia "VB-concentrat" ​​din acest răspuns.
adăugat autor Aaronaught
@Aaronaught Cred că editările tale sunt în mare parte o îmbunătățire. Cu toate că RFC nu este neapărat autoritate, așa cum mulți scriitori CSV nu respectă ea ex Excel nu utilizează întotdeauna o virgulă în fișierele "CSV". De asemenea, răspunsul meu anterior nu a spus deja că clasa ar putea fi utilizată din C #?
adăugat autor MarkJ

A trebuit să folosesc un parser CSV în .NET pentru un proiect în această vară și sa stabilit pe driverul de text Microsoft Jet. Specificați un dosar utilizând un șir de conexiuni, apoi interogați un fișier utilizând o instrucțiune SQL Select. Puteți specifica tipuri puternice utilizând un fișier schema.ini. Nu am facut acest lucru la inceput, dar apoi am obtinut rezultate proaste in cazul in care tipul de date nu a fost imediat evidenta, cum ar fi numerele IP sau o intrare ca "XYQ 3.9 SP1".

O limitare la care am dat seama este că nu poate gestiona numele coloanelor de peste 64 de caractere; ea trunchiază. Acest lucru nu ar trebui să fie o problemă, cu excepția faptului că aveam de-a face cu date de intrare foarte prost concepute. Acesta returnează un ADO.NET DataSet.

Aceasta a fost cea mai bună soluție pe care am găsit-o. Aș fi precaut să mă rostogolească propriul parser CSV, deoarece probabil că aș fi dor de unele cazuri finale și nu am găsit alte pachete gratuite de partajare a CSV pentru .NET acolo.

EDIT: De asemenea, există un singur fișier schema.ini pe director, așa că am adăugat dinamic la el pentru a tasta cu strictețe coloanele necesare. Se va introduce numai coloanele specificate și se va deduce pentru orice câmp nespecificat. Am apreciat cu adevărat acest lucru, pentru că aveam de-a face cu importul unei coloane CSV de 70+ și nu vroiam să precizez fiecare coloană, ci doar cele greșite.

0
adăugat
De ce nu VB.NET construit în parser CSV? msdn.microsoft.com/en-us/library/ & hellip;
adăugat autor MarkJ

Am scris un cod. Rezultatul în datagridviewer arăta bine. Ea analizează o singură linie de text într-un array de obiecte.

    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
adăugat