Frage

Mir ist klar, dass dies eine Neulingsfrage ist, aber ich suche nach einer einfachen Lösung – es scheint, als ob es eine geben sollte.

Wie importiert man eine CSV-Datei am besten in eine stark typisierte Datenstruktur?Wieder einfach = besser.

War es hilfreich?

Andere Tipps

Microsofts TextFieldParser ist stabil und folgt RFC 4180 für CSV-Dateien.Lassen Sie sich davon nicht abschrecken Microsoft.VisualBasic Namensraum;Es handelt sich um eine Standardkomponente im .NET Framework. Fügen Sie einfach einen Verweis auf die globale Komponente hinzu Microsoft.VisualBasic Montage.

Wenn Sie für Windows kompilieren (im Gegensatz zu Mono) und nicht damit rechnen, „kaputte“ (nicht RFC-kompatible) CSV-Dateien analysieren zu müssen, dann wäre dies die offensichtliche Wahl, da es kostenlos, uneingeschränkt und stabil ist. und aktiv unterstützt, was man von FileHelpers größtenteils nicht behaupten kann.

Siehe auch: Wie man:Lesen aus durch Kommas getrennten Textdateien in Visual Basic für ein VB-Codebeispiel.

Verwenden Sie eine OleDB-Verbindung.

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

Wenn Sie ziemlich komplexe Szenarien für die CSV-Analyse erwarten, Denken Sie nicht einmal daran, unseren eigenen Parser zu entwickeln.Es gibt viele hervorragende Tools, z FileHelpers, oder sogar solche von CodeProjekt.

Der Punkt ist, dass dies ein ziemlich häufiges Problem ist und darauf können Sie wetten eine Menge der Softwareentwickler haben sich bereits Gedanken über dieses Problem gemacht und es gelöst.

Brian bietet eine gute Lösung für die Konvertierung in eine stark typisierte Sammlung.

Die meisten der angegebenen CSV-Parsing-Methoden berücksichtigen weder Escape-Felder noch einige andere Feinheiten von CSV-Dateien (z. B. das Trimmen von Feldern).Hier ist der Code, den ich persönlich verwende.Es ist an den Rändern etwas rau und es gibt so gut wie keine Fehlermeldungen.

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

Beachten Sie, dass dies nicht den Randfall von Feldern behandelt, die nicht durch doppelte Anführungszeichen getrennt sind, sondern lediglich eine Zeichenfolge in Anführungszeichen enthalten.Sehen dieser Beitrag für eine etwas bessere Erläuterung sowie einige Links zu einigen geeigneten Bibliotheken.

Ich bin einverstanden mit @Nicht ich. FileHelpers ist gut getestet und bewältigt alle Arten von Grenzfällen, mit denen Sie irgendwann zu kämpfen haben, wenn Sie es selbst tun.Schauen Sie sich an, was FileHelpers macht, und schreiben Sie nur dann Ihre eigenen, wenn Sie absolut sicher sind, dass Sie sich entweder (1) nie mit den Grenzfällen befassen müssen, die FileHelpers macht, oder (2) Sie lieben es, solche Dinge zu schreiben und werden es auch tun Seien Sie überglücklich, wenn Sie solche Dinge analysieren müssen:

1, „Bill“, „Smith“, „Supervisor“, „Kein Kommentar“

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

Hoppla, ich werde nicht zitiert und befinde mich in einer neuen Zeile!

Mir war langweilig, also habe ich einige Sachen, die ich geschrieben habe, geändert.Es wird versucht, das Parsing auf OO-Art zu kapseln und gleichzeitig die Anzahl der Iterationen durch die Datei zu reduzieren, da für jede Datei nur einmal an der Spitze iteriert wird.

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]),
                };
            }
        }
    }
}

Es gibt zwei Artikel auf CodeProject, die Code für eine Lösung bereitstellen, einer davon verwendet StreamReader und eines, das importiert CSV-Daten Verwendung der Microsoft Texttreiber.

Eine gute und einfache Möglichkeit besteht darin, die Datei zu öffnen und jede Zeile in ein Array, eine verknüpfte Liste oder eine Datenstruktur Ihrer Wahl einzulesen.Seien Sie jedoch vorsichtig beim Umgang mit der ersten Zeile.

Das mag Ihnen über den Kopf gehen, aber es scheint auch eine direkte Möglichkeit zu geben, über a darauf zuzugreifen Verbindungszeichenfolge.

Warum nicht versuchen, Python anstelle von C# oder VB zu verwenden?Es gibt ein nettes CSV-Modul zum Importieren, das Ihnen die ganze schwere Arbeit abnimmt.

Ich musste diesen Sommer für ein Projekt einen CSV-Parser in .NET verwenden und habe mich für den Microsoft Jet Text Driver entschieden.Sie geben einen Ordner mithilfe einer Verbindungszeichenfolge an und fragen dann eine Datei mithilfe einer SQL Select-Anweisung ab.Sie können starke Typen mithilfe einer schema.ini-Datei angeben.Zuerst habe ich das nicht gemacht, aber dann bekam ich schlechte Ergebnisse, wenn die Art der Daten nicht sofort erkennbar war, etwa IP-Nummern oder ein Eintrag wie „XYQ 3.9 SP1“.

Eine Einschränkung, auf die ich gestoßen bin, besteht darin, dass Spaltennamen mit mehr als 64 Zeichen nicht verarbeitet werden können.es schneidet ab.Dies sollte kein Problem sein, es sei denn, ich hatte es mit sehr schlecht gestalteten Eingabedaten zu tun.Es gibt ein ADO.NET DataSet zurück.

Das war die beste Lösung, die ich gefunden habe.Ich würde davor zurückschrecken, meinen eigenen CSV-Parser zu entwickeln, da mir wahrscheinlich einige der Endfälle entgehen würden und ich keine anderen kostenlosen CSV-Parsing-Pakete für .NET gefunden habe.

BEARBEITEN:Außerdem kann es nur eine schema.ini-Datei pro Verzeichnis geben, daher habe ich sie dynamisch angehängt, um die benötigten Spalten stark zu typisieren.Es werden nur die angegebenen Spalten stark typisiert und auf alle nicht angegebenen Felder geschlossen.Ich habe das sehr geschätzt, da ich eine fließende CSV-Datei mit mehr als 70 Spalten importieren wollte und nicht jede einzelne Spalte angeben wollte, sondern nur die, die sich schlecht verhielten.

Ich habe einen Code eingegeben.Das Ergebnis im Datagridviewer sah gut aus.Es analysiert eine einzelne Textzeile in eine Arrayliste von Objekten.

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

Wenn Sie garantieren können, dass die Daten keine Kommas enthalten, ist die Verwendung wahrscheinlich die einfachste Möglichkeit String.split.

Zum Beispiel:

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

Möglicherweise gibt es Bibliotheken, die Ihnen helfen könnten, aber das ist wahrscheinlich so einfach wie möglich.Stellen Sie nur sicher, dass die Daten keine Kommas enthalten dürfen, da Sie sie sonst besser analysieren müssen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top