Vra

Ek besef dit is 'n newbie vraag, maar ek is op soek na 'n eenvoudige oplossing - dit lyk asof daar 'n mens moet wees

.

Wat is die beste manier om die invoer 'n CSV lêer in 'n sterk-getikte data struktuur? Weer eenvoudig = beter.

Was dit nuttig?

Oplossing

Ander wenke

Microsoft se TextFieldParser is stabiel en volg RFC 4180 vir CSV lêers. Moenie skrik vir die Microsoft.VisualBasic naamruimte; dit is 'n standaard komponent in die NET Framework, net 'n verwysing by te voeg tot die globale Microsoft.VisualBasic vergadering.

As jy die opstel van is vir Windows (in teenstelling met Mono) en nie verwag om te ontleed "gebroke" (nie-RFC-compliant) CSV lêers, dan sou dit die ooglopende keuse wees, want dit is gratis, onbeperkte , stabiele, en aktief ondersteun, waarvan die meeste kan nie gesê word vir FileHelpers.

Sien ook: Hoe om: Lees Van Comma-Delimited teks lêers in Visual Basic vir 'n VB-kode voorbeeld.

Gebruik 'n OLEDB verbinding.

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();

As jy verwag redelik komplekse scenario's vir CSV parsing, nie eens dink uit rollende ons eie parser . Daar is 'n baie goeie gereedskap daar buite, soos FileHelpers , of selfs dié van CodeProject .

Die punt is dit is 'n redelik algemene probleem en jy kan wed dat 'n baie van sagteware-ontwikkelaars het reeds daaraan gedink en hierdie probleem opgelos.

Brian gee 'n lekker oplossing vir die omskakeling dit na 'n sterk getik versameling.

Die meeste van die CSV parsing metodes gegee nie in ag neem ontsnapping velde of 'n paar van die ander subtiliteite van CSV lêers (soos snoei velde). Hier is die kode wat ek persoonlik gebruik. Dit is 'n bietjie rof rondom die kante en het pretty much geen fout verslagdoening.

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

Let daarop dat dit nie die rand geval van velde nie deliminated deur dubbele aanhalingstekens nie hanteer nie, maar meerley 'n aangehaal string binnekant van dit. Sien hierdie post vir 'n bietjie van 'n beter expanation asook 'n paar skakels na sommige behoorlike biblioteke.

Ek stem saam met @ NotMyself . FileHelpers is goed getoets en hanteer alle vorme van rand gevalle dat jy uiteindelik sal hê om te gaan met as jy doen dit self. Neem 'n blik op wat FileHelpers nie en net skryf jou eie as jy absoluut seker dat óf (1) sal jy nooit nodig het om te hanteer die rand gevalle FileHelpers doen, of (2) jy is lief vir die skryf van hierdie soort dinge en gaan wees verheug wanneer jy dinge ontleed soos volg:

1, "Bill", "Smith", "toesighouer", "geen kommentaar"

2, 'Drake,', 'O'Malley, "opsigter,

Oeps, ek is nie aangehaal en ek is op 'n nuwe reël!

Ek was verveeld so ek verander 'n paar dinge wat ek geskryf het. Dit probeer om die ontleding omsluit in 'n OO wyse whle kap op die bedrag van iterasies deur die lêer, dit net iterate weer by die top 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]),
                };
            }
        }
    }
}

Daar is twee artikels oor CodeProject dat kode vir 'n oplossing, een wat gebruik maak van StreamReader en een wat invoer CSV data met behulp van die Microsoft teks Driver .

'n goeie eenvoudige manier om dit te doen, is om die lêer oop te maak, en lees elke lyn in 'n skikking, gekoppel lys, data-struktuur-van-jou-keuse. Wees versigtig oor die hantering van die eerste reël al is.

Dit kan wees oor jou kop, maar dit lyk asof daar 'n direkte manier om dit te bekom, asook die gebruik van 'n verband string .

Hoekom nie probeer om met behulp Python in plaas van C # of VB? Dit het 'n mooi CSV module te invoer wat al die swaar werk doen vir jou.

Ek moes 'n CSV parser gebruik in NET vir 'n projek in die somer en gevestig op die Microsoft Jet teks Driver. Jy gee 'n gids met behulp van 'n verband string, dan bevraagteken 'n lêer met 'n SQL Kies verklaring. Jy kan sterk tipes spesifiseer met behulp van 'n schema.ini lêer. Ek het dit nie gedoen op die eerste, maar dan is ek kry slegte resultate waar die tipe van die data was nie onmiddellik duidelik, soos IP nommers of 'n inskrywing soos "XYQ 3.9 SP1".

Een beperking Ek hardloop in, is dat dit nie kan hanteer kolom name bo 64 karakters; dit truncates. Dit moet nie 'n probleem te wees nie, behalwe as ek kon doen met 'n baie swak ontwerp insette data. Dit gee 'n ADO.NET dataset.

Dit was die beste oplossing wat ek gevind. Ek sou versigtig vir rollende my eie CSV parser wees, want ek sou waarskynlik 'n paar van die einde gevalle mis, en ek het nie enige ander gratis CSV parsing pakkette vir NET daar buite vind.

EDIT: Ook, daar kan net een schema.ini lêer per gids wees, so ek dinamies bygevoeg om dit te sterk tik die nodige kolomme. Dit sal net sterk-tik die gespesifiseerde kolomme, en lei vir enige ongespesifiseerde veld. Ek dit regtig waardeer, as ek die hantering van die invoer van 'n vloeistof 70 + kolom CSV en wou nie elke kolom, net die wangedrag ingespan kinders spesifiseer.

Ek getik in 'n paar kode. Die resultaat in die datagridviewer het goed gelyk. Dit ontleed 'n enkele lyn van teks na 'n Array List van voorwerpe.

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

As jy kan waarborg dat daar geen kommas in die data, dan is die eenvoudigste manier sou waarskynlik wees om te gebruik String.split .

Byvoorbeeld:

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

Daar mag biblioteke wat jy kan gebruik om te help, maar dit is waarskynlik so eenvoudig as wat jy kan kry. Maak net seker dat jy nie kan kommas in die data het, anders sal jy nodig het om dit beter te ontleed.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top