2014-11-06 13 views
14

Ho un file CSV con un elenco di dati vari (datetime, decimal). Esempio di linea da CSV:Leggi CSV nell'elenco di oggetti

Date,Open,High,Low,Close,Volume,Adj Close //I need to skip this first line as well 
2012-11-01,77.60,78.12,77.37,78.05,186200,78.05 

Ho una lista di oggetti creati in cui voglio leggere ciascuna delle righe. Il costruttore per gli oggetti è sotto, ognuno dei campi di ciascuna linea CSV è usato e assegnato qui.

public DailyValues(DateTime date, decimal open, decimal high, decimal low, 
     decimal close, decimal volume, decimal adjClose) 
     : this() 
    { 
     Date = date; 
     Open = open; 
     High = high; 
     Low = low; 
     Close = close; 
     Volume = volume; 
     AdjClose = adjClose; 
    } 

    List<DailyValues> values = new List<DailyValues>(); 

C'è un modo semplice per leggere ogni riga del CSV in lista values e opportunamente assegnare ciascun attributo (cioè data, aperto, alto)?

+0

C'è una classe 'TextFieldParser' nascosta nello spazio dei nomi' Microsoft.VisualBasic.FileIO'. Anche se dice VisualBasic, puoi usarlo anche con C#. – cost

+0

Lo farei tramite Linq - vedi questo post: http://stackoverflow.com/questions/3497699/csv-to-object-model-mapping – Kevin

+0

@Kevin Che non è completamente conforme alle specifiche CSV. Ci sarebbero problemi se ci fossero dei dati che contenevano una virgola all'interno dei dati – cost

risposta

34

Perché non analizzarli solo in modo esplicito? Hai un numero limitato di proprietà, quindi non è molto difficile. Invece di usare un costruttore che richiede molti argomenti, ho usato un metodo statico che restituisce una nuova istanza DailyValues come tipo di ritorno. Questo è simile a DateTime.FromBinary ecc

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.IO; 

namespace CsvDemo 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<DailyValues> values = File.ReadAllLines("C:\\Users\\Josh\\Sample.csv") 
              .Skip(1) 
              .Select(v => DailyValues.FromCsv(v)) 
              .ToList(); 
     } 
    } 

    class DailyValues 
    { 
     DateTime Date; 
     decimal Open; 
     decimal High; 
     decimal Low; 
     decimal Close; 
     decimal Volume; 
     decimal AdjClose; 

     public static DailyValues FromCsv(string csvLine) 
     { 
      string[] values = csvLine.Split(','); 
      DailyValues dailyValues = new DailyValues(); 
      dailyValues.Date = Convert.ToDateTime(values[0]); 
      dailyValues.Open = Convert.ToDecimal(values[1]); 
      dailyValues.High = Convert.ToDecimal(values[2]); 
      dailyValues.Low = Convert.ToDecimal(values[3]); 
      dailyValues.Close = Convert.ToDecimal(values[4]); 
      dailyValues.Volume = Convert.ToDecimal(values[5]); 
      dailyValues.AdjClose = Convert.ToDecimal(values[6]); 
      return dailyValues; 
     } 
    } 
} 

Naturalmente, è ancora possibile aggiungere un costruttore di default, e si vuole aggiungere la gestione delle eccezioni nel caso in cui l'analisi non riesce (si può anche usare TryParse per questo).

  • Il File.ReadAllLines legge tutte le righe dal file CSV in un array di stringhe.
  • Il .Skip(1) salta la riga di intestazione.
  • .Select(v => DailyValues.FromCsv(v)) utilizza Linq per selezionare ciascuna riga e creare una nuova istanza DailyValues utilizzando il metodo FromCsv. Questo crea un tipo System.Collections.Generic.IEnumerable<CsvDemo.DailyValues>.
  • Infine, lo .ToList() conversa con lo IEnumerable su un List per abbinare il tipo desiderato.

Invece di usare LINQ si potrebbe avere utilizzato semplicemente un ciclo foreach per aggiungere ogni DailyValues istanza alla vostra lista.

+0

Mi piace questo approccio e, se necessario, è possibile implementare l'analisi CSV più complessa in FromCsv. +1 – Kevin

+0

Questo è molto più semplice di qualsiasi altra soluzione fornita su StackOverflow :) –

+1

Semplice ed efficace! +1 – anil