Writing data to a CSV file is a common operation. In this article, we will show how to easily write data to a CSV file in C# using the CSVHelper NuGet package. Even though there are different methods of doing this, using this package is by far the most common and intuitive way of doing it. We are also going to look at some ways of configuring this.
There are lots of topics to cover, so let’s get started!
VIDEO: Working With CSV Files in C#.
Creation of a New Project and a Person Class
As writing to a CSV file doesn’t involve any web technologies, we will create the simplest kind of application, a Console App in C#.
After the project creation, let’s create a Person
class:
public class Person { public int Id { get; set; } public string Name { get; set; } public bool IsLiving { get; set; } }
Within our Main
method, let’s now make a list of Person
objects which we will soon write to our CSV file:
var myPersonObjects = new List<Person>() { new Person { Id = 1, IsLiving = true, Name = "John" }, new Person { Id = 2, IsLiving = true, Name = "Steve" }, new Person { Id = 3, IsLiving = true, Name = "James" } };
Writing Into a CSV File in C# with Default Settings
CSVHelper has emerged as the defacto standard way to write CSV in C# and is very easy to use:
using (var writer = new StreamWriter("filePersons.csv")) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(myPersonObjects); }
This is all to it: We first make a StreamWriter
which helps us to write things in the first place. You can look at the Files, StreamWriter and StreamReader article to read more about the StreamWriter class and the reason for using the using
blocks. We then use this to construct our CsvWriter
which is coming from the CsvHelper Nuget package.
When constructing the CsvWriter
, pay attention that we pass in CultureInfo.InvariantCulture
. We won’t be going into detail about what this is, just remember that it writes it in an invariant (neutral) way, as different cultures have different ways of representing numbers and dates. If you like, you could also specify your own culture, so instead of CultureInfo.InvariantCulture
, you could pass in new CultureInfo("fr-FR")
if you would like to write it in French (France) format. This becomes especially relevant when reading in data, as you need the same CultureInfo
to read it in which it was also written.
If we run this program, a CSV file named filePersons.csv
will appear where our executable also is. Let’s open this file (with Notepad++ for instance) and see how it looks like:
Custom Names and Ordering of Columns
In the picture above, we can see that the header names and order is exactly the same as how we defined them in our Person
class. By annotating the properties in that class, we can change the outputted header name or the ordering, which works by annotating the properties by the Name
or by the Index
annotations:
public class Person { [Name("Identifier")] [Index(0)] public int Id { get; set; } [Index(2)] public string Name { get; set; } [Index(1)] public bool IsLiving { get; set; } }
By running the program again, our CSV file will change – both the ordering as well as the name of the Id
property has changed:
Identifier,IsLiving,Name 1,True,John 2,True,Steve 3,True,James
Of course, we can also decide to omit the headers altogether. In that case, we need to pass in a CsvConfiguration
object while constructing our CsvWriter. Within this CsvConfiguration
object, we set the HasHeaderRecord
property to false:
var configPersons = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = false }; using (var writer = new StreamWriter("filePersons.csv")) using (var csv = new CsvWriter(writer, configPersons)) { csv.WriteRecords(myPersonObjects); }
When we run the program again, we can see the headers are no longer there.
Appending Data
We have to pay attention that every time we run our program, the old CSV file is overwritten. Sometimes we would like that data to be appended at the end of a file, instead of overwriting existing data. CsvHelper doesn’t provide a method to do this, as opening/writing to a file is not the responsibility of CsvHelper. We can achieve this by using a FileStream
and then construct our StreamWriter
by using that FileStream
. Of course. we shouldn’t write the headers (again) if we are appending:
var configPersons = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = false }; using (var stream = File.Open("filePersons.csv", FileMode.Append)) using (var writer = new StreamWriter(stream)) using (var csv = new CsvWriter(writer, configPersons)) { csv.WriteRecords(myPersonObjects); }
Let’s run our project to see the output. We have to make sure the file is not open anywhere as this will throw an exception:
1,True,John 2,True,Steve 3,True,James 1,True,John 2,True,Steve 3,True,James
Appending to an existing file is tricky though, as the ordering might have changed when we append, or we might have added new properties. We should make sure to keep this into account when appending.
Formatting Dates While Writing Into a CSV File in C#
Let’s now add a new property to our Person
class:
public DateTime DateOfBirth { get; set; }
And now let’s add a code to write this data to a new CSV file:
var myPersonObjects = new List<Person>() { new Person { Id = 1, IsLiving = true, Name = "John", DateOfBirth = Convert.ToDateTime("03/05/2006") }, new Person { Id = 2, IsLiving = true, Name = "Steve", DateOfBirth = Convert.ToDateTime("03/09/1998") }, new Person { Id = 3, IsLiving = true, Name = "James", DateOfBirth = Convert.ToDateTime("03/08/1994") } }; using (var writer = new StreamWriter("filePersonsWithDoB.csv")) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(myPersonObjects); }
How does it get written when we write this? Let’s run it and see!
Id,Name,IsLiving,DateOfBirth 1,John,True,03/05/2006 00:00:00 2,Steve,True,03/09/1998 00:00:00 3,James,True,03/08/1994 00:00:00
Hmm, not exactly what we had in mind actually, as we are not interested in the exact time, but rather are interested in the date, in the yyyy-MM-dd
format. Luckily, this is done easily as well using annotations:
[Format("yyyy-MM-dd")] public DateTime DateOfBirth { get; set; }
We can run it again, to see the change:
Id,Name,IsLiving,DateOfBirth 1,John,True,2006-03-05 2,Steve,True,1998-03-09 3,James,True,1994-03-08
And it works.
Conclusion
In this article, we’ve seen how easy it is to write data to a CSV file using CSVHelper. We also saw that it is very easy to change the header names, to show or hide the header, or to change the ordering of the columns. Finally, we saw that we can format dates in any way we want.
Until the next one.
All the best.