La lettura di file CSV utilizzando C#

Sto scrivendo una semplice domanda di importazione e bisogno di leggere un file CSV, mostrare il risultato in un DataGrid e mostra danneggiato righe del file CSV in un’altra griglia. Per esempio, mostrano le linee che sono più brevi di 5 valori in un’altra griglia. Sto cercando di farlo come questo:

StreamReader sr = new StreamReader(FilePath);
importingData = new Account();
string line;
string[] row = new string [5];
while ((line = sr.ReadLine()) != null)
{
    row = line.Split(',');

    importingData.Add(new Transaction
    {
        Date = DateTime.Parse(row[0]),
        Reference = row[1],
        Description = row[2],
        Amount = decimal.Parse(row[3]),
        Category = (Category)Enum.Parse(typeof(Category), row[4])
    });
}

ma è molto difficile operare su matrici in questo caso. C’è un modo migliore per dividere i valori?

InformationsquelleAutor ilkin | 2010-08-17

 

10 Replies
  1. 339

    Non reinventare la ruota. Approfittare di ciò che già .NET BCL.

    • aggiungere un riferimento a Microsoft.VisualBasic (sì, si dice VisualBasic, ma funziona in C# come ben ricordate che alla fine tutto è solo IL)
    • utilizzare il Microsoft.VisualBasic.FileIO.TextFieldParser classe per analizzare file CSV

    Ecco il codice di esempio:

    using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
    {
        parser.TextFieldType = FieldType.Delimited;
        parser.SetDelimiters(",");
        while (!parser.EndOfData) 
        {
            //Processing row
            string[] fields = parser.ReadFields();
            foreach (string field in fields) 
            {
                //TODO: Process field
            }
        }
    }

    Funziona alla grande per me, nei miei progetti di C#.

    Qui ci sono alcuni link/informazioni:

    • Vorrei DAVVERO che ci fosse un modo che non uso VB librerie, ma questo ha funzionato perfettamente! Grazie!
    • +1: ho appena rotto la lumenworks Veloce CSV lettore su un 53Mb file. Guarda come la linea di cache non è riuscito dopo 43,000 righe e strapazzate buffer. Provato il VB TextFieldParser e ha fatto il trucco. Grazie
    • +1 Grande risposta, come ho trovato molte persone non conoscono questa classe esiste. Una cosa per i futuri spettatori di nota è che l’impostazione di parser.TextFieldType = FieldType.Delimited; non è necessario se si chiama parser.SetDelimiters(",");, come il metodo imposta il TextFieldType proprietà per voi.
    • Controllare anche questo: dotnetperls.com/textfieldparser. TextFieldParser ha prestazioni inferiori a Stringa.Split e StreamReader. Tuttavia, c’è una grande differenza tra la stringa.Split e TextFieldParser. TextFieldParser maniglie casi strani come avere una virgola in una colonna: si può fare il nome di una colonna come "text with quote"", and comma", e si può ottenere il valore corretto text with quote", and comma invece erroneamente valori separati. Quindi, si consiglia di optare per la Stringa.Dividere se csv è molto semplice.
    • Ho usato questo per analizzare i file di oltre 12 GB e non interrompe mai.
    • Si noti che potrebbe essere necessario aggiungere un riferimento a Microsoft.VisualBasic per utilizzare questo. Fai clic destro sul vostro progetto in Visual Studio, quindi scegliere Aggiungi > di Riferimento, e la casella di controllo di Microsoft.VisualBasic.
    • Per i principianti come me che non sapevo nemmeno che cosa IL è, check-out codeproject.com/Articles/3778/….
    • impressionante, basta controllare se il campo non è null. Per annullare una possibile eccezione di riferimento null
    • “Microsoft.VisualBasic.FileIO.TextFieldParser” vi sarà anche in grado di analizzare .i file txt, è molto utile.
    • se c’è un errore, devo chiudere nulla, o il codice di cui sopra, chiudere automaticamente tutto ciò che deve essere chiuso?
    • Il parser è in un using di blocco, in modo che assicura che sarà smaltito. Vedi qui per maggiori dettagli msdn.microsoft.com/en-us/library/…
    • Um, @Doug, penso che è lo scopo principale.
    • Se NON dovesse FUNZIONARE qualsiasi tipo di dati contiene una VIRGOLA. Normalmente CSV avrebbe Virgolette intorno a ogni valore.
    • è corretta. Inoltre : se i dati contengono mista campi, ad esempio 1,"two", poi lo soffoca. I campi possono essere racchiusi tra virgolette sempre, o mai. È possibile controllare in TextFieldParser.HasFieldsEnclosedInQuotes . Altrimenti sarebbe perfetto. E ‘ davvero deludente reale CSV di supporto non core nel 2017.
    • A proposito, questo “VB” il codice è il codice utilizzato in Excel, Access e SQL Server, quindi è molto ben collaudata.
    • using (var parser = FileIO.FileSystem.OpenTextFieldParser(@"c:\1.csv", ",")) il primo alcune dichiarazioni prima del ciclo
    • Questo NON funziona se il testo contiene una virgola, basta impostare TextFieldParser.HasFieldsEnclosedInQuotes = true. Questa riga per riga approccio non significa tuttavia che non può essere a capo, embedded, che è valido in CSV, a seconda di chi si chiede.

  2. 32

    La mia esperienza è che ci sono molti diversi formati csv. Appositamente come gestire la fuga di citazioni e di delimitatori all’interno di un campo.

    Queste sono le varianti che mi sono imbattuto in:

    • le quotazioni sono quotate e raddoppiato (excel), cioè il 15″ -> campo1,”15″””,campo3
    • citazioni non sono cambiato, a meno che il campo è citato per qualche altro motivo. cioè 15″ -> campo1,15″,fields3
    • le quotazioni sono fuggiti con \. cioè 15″ -> campo1″15\””,campo3
    • citazioni non sono cambiato affatto (questo non è sempre possibile, per analizzare correttamente)
    • delimitatore è citato (excel). cioè a,b -> campo1,”a,b”,campo3
    • delimitatore è fuggito con \. cioè a,b -> campo1,a\,b,campo3

    Ho provato molti dei csv esistente parser, ma non c’è uno solo che può gestire le varianti mi sono imbattuto in. È anche difficile trovare fuori dalla documentazione di cui fuggire varianti il parser di supporto.

    Nei miei progetti ho ora usare il VB TextFieldParser un custom o splitter.

    • Amore questa risposta per i casi di test che hai fornito!
    • Il problema principale è che la maggior parte delle implementazioni non si preoccupano RFC 4180 che descrive il formato CSV e come delimitatori deve essere sfuggito.
    • RFC-4180 è dal 2005 che sembra vecchio, ma ricordate: l’ .Net framework è stato il primo, uscito nel 2001. Inoltre, le Rfc non sono sempre norme ufficiali, e in questo caso non hanno lo stesso peso come, per dire, ISO-8601 o RFC-761.
  3. 21

    Mi raccomando CsvHelper da Nuget.

    (Aggiungere un riferimento a Microsoft.VisualBasic semplicemente non si sente di destra, non è solo brutto, è probabilmente non è anche cross-platform.)

    • E ‘ esattamente come cross-platform come C# e.
    • sbagliato, Microsoft.VisualBasic.dll in Linux deriva da sorgenti Mono, che ha una diversa implementazione di Microsoft e ci sono alcune cose che non vengono attuate, per esempio: stackoverflow.com/questions/6644165/…
    • (Plus, linguaggio VB non ha mai avuto qualsiasi intervento della società che sono stati coinvolti nella creazione/sviluppo del progetto Mono, quindi è molto indietro in termini di sforzi, rispetto al C# ecosistema/utensili.)
    • Dopo aver giocato con entrambi, mi piacerebbe aggiungere che CsvHelper viene fornito con un built-in fila per classe mapper; esso permette variazioni nelle intestazioni di colonna (se presente), e anche a quanto pare variazioni nell’ordine delle colonne (anche se non ho provato l’ultimo di me). Tutto sembra molto più “alto livello” di TextFieldParser.
  4. 12

    A volte usando le librerie sono freddi quando non si desidera che reinventare la ruota, ma in questo caso si può fare lo stesso lavoro con meno righe di codice e di più facile lettura rispetto all’utilizzo di librerie.
    Qui è un approccio diverso che trovo molto facile da usare.

    1. In questo esempio, io uso StreamReader per leggere il file
    2. Regex per rilevare il delimitatore di ogni riga(s).
    3. Un array di raccogliere le colonne di indice 0 per n

    using (StreamReader reader = new StreamReader(fileName))
        {
            string line; 
    
            while ((line = reader.ReadLine()) != null)
            {
                //Define pattern
                Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
    
                //Separating columns to array
                string[] X = CSVParser.Split(line);
    
                /* Do something with X */
            }
        }
    • Certo che ha problemi con i dati che contiene le nuove linee?
    • Ora CSV file di dati non si conoscono per il contenimento delle righe vuote tra i dati, ma se si dispone di una fonte che, in quel caso mi sarebbe stato semplice fare una semplice espressione regolare, prova a togliere gli spazi o righe che contengono nulla prima di eseguire il lettore. qui troverai diversi esempi: stackoverflow.com/questions/7647716/…
    • Sicuramente un char basato su un approccio più naturale per questo tipo di problema di una regex. A seconda della presenza di virgolette il comportamento dovrebbe essere diverso.
  5. 6

    CSV può ottenere complicato reale veloce.

    Usare qualcosa di robusto e ben collaudato:

    FileHelpers:
    http://www.filehelpers.net

    Il FileHelpers sono un libero e facile da usare .Libreria di rete per importare/esportare i dati da una lunghezza fissa o delimitato da record nel file, stringhe o torrenti.

    • Penso FileHelper sta cercando di fare troppo in una volta. I file di analisi è un 2 fase di processo cui per la prima volta le linee di divisione in campi e quindi analizzare i campi di dati. Combinando le funzioni rende difficile gestire le cose come master-detail e il filtro di riga.
  6. 4

    Un altro uno di questa lista, Cinchoo ETL – una libreria open source per leggere e scrivere file CSV

    Per un file CSV di esempio di seguito

    Id, Name
    1, Tom
    2, Mark

    Velocemente si può caricare tramite la biblioteca, come di seguito

    using (var reader = new ChoCSVReader("test.csv").WithFirstLineHeader())
    {
       foreach (dynamic item in reader)
       {
          Console.WriteLine(item.Id);
          Console.WriteLine(item.Name);
       }
    }

    Se si dispone di POCO la classe corrispondente file CSV

    public class Employee
    {
       public int Id { get; set; }
       public string Name { get; set; }
    }

    Si può utilizzare per caricare il file CSV, come di seguito

    using (var reader = new ChoCSVReader<Employee>("test.csv").WithFirstLineHeader())
    {
       foreach (var item in reader)
       {
          Console.WriteLine(item.Id);
          Console.WriteLine(item.Name);
       }
    }

    Si prega di consultare articoli CodeProject su come usarlo.

    Disclaimer: io sono l’autore di questa raccolta

    • Ciao, riesci a caricare csv di tabella Sql – non so l’intestazione del CSV tabella di prima mano. Solo specchio di cosa è fatta csv di tabella Sql
    • Sì, è possibile. si prega di vedere questo link stackoverflow.com/questions/20759302/…
  7. 2
    private static DataTable ConvertCSVtoDataTable(string strFilePath)
            {
                DataTable dt = new DataTable();
                using (StreamReader sr = new StreamReader(strFilePath))
                {
                    string[] headers = sr.ReadLine().Split(',');
                    foreach (string header in headers)
                    {
                        dt.Columns.Add(header);
                    }
                    while (!sr.EndOfStream)
                    {
                        string[] rows = sr.ReadLine().Split(',');
                        DataRow dr = dt.NewRow();
                        for (int i = 0; i < headers.Length; i++)
                        {
                            dr[i] = rows[i];
                        }
                        dt.Rows.Add(dr);
                    }
    
                }
    
                return dt;
            }
    
            private static void WriteToDb(DataTable dt)
            {
                string connectionString =
                    "Data Source=localhost;" +
                    "Initial Catalog=Northwind;" +
                    "Integrated Security=SSPI;";
    
                using (SqlConnection con = new SqlConnection(connectionString))
                    {
                        using (SqlCommand cmd = new SqlCommand("spInsertTest", con))
                        {
                            cmd.CommandType = CommandType.StoredProcedure;
    
                            cmd.Parameters.Add("@policyID", SqlDbType.Int).Value = 12;
                            cmd.Parameters.Add("@statecode", SqlDbType.VarChar).Value = "blagh2";
                            cmd.Parameters.Add("@county", SqlDbType.VarChar).Value = "blagh3";
    
                            con.Open();
                            cmd.ExecuteNonQuery();
                        }
                    }
    
             }
    • da dove hai fatto copia di questa soluzione?
  8. 0

    Prima di tutto è necessario capire che cosa è CSV e come scriverlo.

    1. Ogni stringa ( /r/n ) è la successiva “tabella” di riga.
    2. “Tabella” cellule separate da delimitatore di simbolo. La maggior parte dei simboli più usati è \t o ,
    3. Ogni cella può eventualmente contenere questo delimitatore simbolo (cella deve iniziare con citazioni simbolo e termina con questo simbolo, in questo caso)
    4. Ogni cella può forse contiene /r/n sybols (cella deve iniziare con citazioni simbolo e termina con questo simbolo, in questo caso)

    Il modo più semplice per C#/Visual Basic per lavorare con i file CSV è quello di utilizzare standard Microsoft.VisualBasic biblioteca. Hai solo bisogno di aggiungere un riferimento necessario, e la seguente stringa per la classe:

    using Microsoft.VisualBasic.FileIO;

    Sì, si può usare in C#, non preoccuparti. Questa libreria può leggere relativamente grande di file e supporta tutti bisogno di regole, in modo che si sarà in grado di lavorare con tutti i file CSV.

    Qualche tempo fa avevo scritto semplice classe per il CSV di lettura/scrittura basato sulla libreria. Utilizzando questa semplice classe che si sarà in grado di lavorare con i CSV come con 2 dimensioni della matrice.
    Potete trovare la mia classe al seguente link:
    https://github.com/ukushu/DataExporter

    Semplice esempio di utilizzo:

    Csv csv = new Csv("\t");//delimiter symbol
    
    csv.FileOpen("c:\\file1.csv");
    
    var row1Cell6Value = csv.Rows[0][5];
    
    csv.AddRow("asdf","asdffffff","5")
    
    csv.FileSave("c:\\file2.csv");
  9. 0

    Per completare le risposte precedenti, si può avere bisogno di una collezione di oggetti dal suo File CSV, sia analizzata dal TextFieldParser o il string.Split metodo, e quindi ogni riga convertita in un oggetto tramite la Riflessione. Ovviamente prima necessario definire una classe che corrisponde le righe del file CSV.

    Ho usato la semplice CSV Serializzatore da Michael Kropat trovato qui: Classe generica al CSV di tutte le proprietà)
    e riutilizzati per i suoi metodi per ottenere i campi e le proprietà dell’voluto classe.

    Ho deserializzare il mio file CSV con il seguente metodo:

    public static IEnumerable<T> ReadCsvFileTextFieldParser<T>(string fileFullPath, string delimiter = ";") where T : new()
    {
        if (!File.Exists(fileFullPath))
        {
            return null;
        }
    
        var list = new List<T>();
        var csvFields = GetAllFieldOfClass<T>();
        var fieldDict = new Dictionary<int, MemberInfo>();
    
        using (TextFieldParser parser = new TextFieldParser(fileFullPath))
        {
            parser.SetDelimiters(delimiter);
    
            bool headerParsed = false;
    
            while (!parser.EndOfData)
            {
                //Processing row
                string[] rowFields = parser.ReadFields();
                if (!headerParsed)
                {
                    for (int i = 0; i < rowFields.Length; i++)
                    {
                        //First row shall be the header!
                        var csvField = csvFields.Where(f => f.Name == rowFields[i]).FirstOrDefault();
                        if (csvField != null)
                        {
                            fieldDict.Add(i, csvField);
                        }
                    }
                    headerParsed = true;
                }
                else
                {
                    T newObj = new T();
                    for (int i = 0; i < rowFields.Length; i++)
                    {
                        var csvFied = fieldDict[i];
                        var record = rowFields[i];
    
                        if (csvFied is FieldInfo)
                        {
                            ((FieldInfo)csvFied).SetValue(newObj, record);
                        }
                        else if (csvFied is PropertyInfo)
                        {
                            var pi = (PropertyInfo)csvFied;
                            pi.SetValue(newObj, Convert.ChangeType(record, pi.PropertyType), null);
                        }
                        else
                        {
                            throw new Exception("Unhandled case.");
                        }
                    }
                    if (newObj != null)
                    {
                        list.Add(newObj);
                    }
                }
            }
        }
        return list;
    }
    
    public static IEnumerable<MemberInfo> GetAllFieldOfClass<T>()
    {
        return
            from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
            where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType)
            let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute))
            orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name
            select mi;            
    }

Lascia un commento