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.

To download the source code for this article, you can visit our GitHub repository.

There are lots of topics to cover, so let’s get started!

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:

Result of writing to a CSV file in C#

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.