57

I have a CSV file with a listing of varied data(datetime, decimal). Sample line from CSV:

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 

I have a list of objects created that I want to read each of the lines into. The constructor for the objects is below, each of the fields from each CSV line is used and assigned here.

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

Is there an easy way to read each line of the CSV into my list values and appropriately assign each attribute (i.e. date, open, high)?

4
  • There is a TextFieldParser class hidden away in the Microsoft.VisualBasic.FileIO namespace. Even though it says VisualBasic, you can use it with C# too. Commented Nov 6, 2014 at 22:24
  • I would do this via Linq - see this post: stackoverflow.com/questions/3497699/csv-to-object-model-mapping Commented Nov 6, 2014 at 22:24
  • @Kevin That doesn't fully conform to the CSV spec. There would be problems if there was any data that contained a comma within the data Commented Nov 6, 2014 at 22:28
  • 1
    @cost - I completely agree, but looking at the example data I see no problems. Commented Nov 6, 2014 at 22:29

1 Answer 1

168

Why not just parse these explicitly? You have a limited number of properties, so it's not very difficult. Instead of using a constructor requiring many arguments, I used a static method that returns a new DailyValues instance as it's return type. This is similar to DateTime.FromBinary etc.

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

Of course, you can still add a default constructor, and you will want to add exception handling in case the parsing fails (you can also use TryParse for that).

  • The File.ReadAllLines reads all lines from the CSV file into a string array.
  • The .Skip(1) skips the header line.
  • The .Select(v => DailyValues.FromCsv(v)) uses Linq to select each line and create a new DailyValues instance using the FromCsv method. This creates a System.Collections.Generic.IEnumerable<CsvDemo.DailyValues> type.
  • Finally, the .ToList() convers the IEnumerable to a List to match the type you want.

Instead of using Linq you could have simply used a foreach loop to add each DailyValues instance to your list.

Sign up to request clarification or add additional context in comments.

11 Comments

I like this approach, and you could implement more complex CSV parsing in FromCsv if required. +1
This is much simpler than any other solution provided on stackoverflow :)
Simple and Effective ! +1
Isn't in flawed though, what if there was a comma in the actual data, example "Los Angeles, CA" would split across 2 seperate fields. OK if you know the data doesnt contain strings though.
@Paul: Definitely, but most implementations have different standards to handle comma/special character escaping (i.e. one way is to wrap values containing commas in double quotes, as you said). So you'd probably be better off modifying the logic for those cases when they are known, or using a library that handles CSV parsing instead. My answer assumes the simplest case as described in the question.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.