[C#][.NET] Shos.CsvHelper (simple library for reading and writing CSV)

2020年1月22日

CSV Icon

I wrote a simple library for reading and writing CSV (the Values Comma-Separated or Character-Separated Values).

The csv format file is sometimes necessary because it can be displayed / edited with Excel and is simple.
There are other libraries that read and write csv already, but I tried to make it simpler.

With this library, you can read and write plain object collections (IEnumerable<Something>) in csv format.

How to use

Overview of how to use

First, prepare something IEnumerable<TElement>:

    An IEnumerable<TElement> something
    IEnumerable<ToDo> toDoes = new ToDoList();

For example this class:

    class ToDoList : IEnumerable<ToDo> // sample collection
    {
        public IEnumerator<ToDo> GetEnumerator()
        {
            yield return new ToDo { Id = 1, Title = "filing tax returns", Deadline = new DateTime(2018, 12, 1) };
            yield return new ToDo { Id = 2, Title = "report of a business trip", Detail = "\"ASAP\"", DaySpan = new DaySpan(3), Priority = Priority.High };
            yield return new ToDo { Id = 3, Title = "expense slips", Detail = "book expenses: \"C# 6.0 and the .NET 4.6 Framework\",\"The C# Programming\"", Priority = Priority.Low, Done = true };
            yield return new ToDo { Id = 4, Title = " wish list ", Detail = " \t (1) \"milk\"\n \t (2) shampoo\n \t (3) tissue ", Priority = Priority.High };
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

You can write this as:

    const string csvFileName = "todo.csv";
    await toDoes.WriteCsvAsync(csvFileName);

The resulting csv file looks like this:

  • If the value contains commas, delimiters, newlines, or double quotes, these are enclosed in double quotes.
  • Double quotations in the value are replaced by two double quotations.
Id,Title,Deadline,Done,Priority,Details,DaySpan
1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/12 13:13:01,False,High,"""ASAP""",3
3,expense slips,2017/07/12 13:13:01,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0
Open the created csv file in Excel
Open the created csv file in Excel

You also can read a csv file like this:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFileName);

Things to read and write

Public properties with both “get” and “set” of each element in the collection are read and written to csv files.

When writing:

When writing, it is converted to a string with the “ToString()” method regardless of the type.

When reading:

When reading, the string type is left as is, and enum (enumeration type) is read as the value of the string.
For other types, it tries to change the string to a value using “TryParse” or “Parse”.
Types that cannot do either of these will not be read.

Properties that have both “get” and “set” and are of one of the following types are read:

  • String type
  • enum
  • A type that has a default constructor and can be “TryParse” or “Parse”
    (Basic numeric types such as int, DateTime, user-defined types with “TryParse” or “Parse”)
Other rules
  • Properties with the [CsvIgnore ()] attribute are not read or written.
  • Properties with the [ColumnName(“Column name”)] attribute will be changed to the one with the specified column name in the csv file.

For example, the above ToDo class is like this:

    // Sample element type
    // Properties marked with ✔ will be read / written
    // Properties marked with ✖ won't be read / written
    class ToDo
    {
        public int      Id       { get; set; }                     // ✔
        public string   Title    { get; set; } = "";               // ✔
        public DateTime Deadline { get; set; } = DateTime.Now;     // ✔
        public bool     Done     { get; set; }                     // ✔
        public Priority Priority { get; set; } = Priority.Middle;  // ✔ user-defined enum 
        [ColumnName("Details")]
        public string   Detail   { get; set; } = "";               // ✔ [ColumnName ("Details")] changes the column name in the csv file to "Details"
        public DaySpan  DaySpan  { get; set; }                     // ✔ User-defined type with "Parse" (without "TryParse")
        [CsvIgnore()]
        public string   Option   { get; set; } = "";               // ✖ Ignored because [CsvIgnore()] is attached
        public string   Version => "1.0";                          // ✖ Ignored because it is only a get property
    }

The user-defined types used in the above ToDo class are as follows:

    // User-defined enum example
    enum Priority { High, Middle, Low }

    // Example of a user-defined type with "Parse" (without "TryParse")
    struct DaySpan
    {
        public int Value { get; private set; }

        public DaySpan(int value) => Value = value;
        public static DaySpan Parse(string text) => new DaySpan(int.Parse(text));
        public override string ToString() => Value.ToString();
    }

With or without header

csv can be read and written even if it has no header part (eg the first line of the csv file above).

However, if there is a header part, it can be read by collating the header part even if the column is switched, but if there is no header part, it cannot be read if the column is switched.

Example of writing without a header:

    await toDoes.WriteCsvAsync(csvFilePathName: csvFileName, hasHeader: false);

Csv file created:

1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/06 18:08:13,False,High,"""ASAP""",3
3,expense slips,2017/07/06 18:08:13,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0
Open the created csv file in Excel
Open the created csv file in Excel

To read a csv file without a header:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFilePathName: csvFileName, hasHeader: false);

Other specification methods

The character code can be changed (default is UTF8).

    CsvSerializer.Encoding = Encoding.GetEncoding(0);

The delimiter can also be changed (default is ‘,’).

    CsvSerializer.Separator = '\t';

You can also use stream instead of file name.

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream);

You can leave the stream open after reading or writing by specifying leaveOpen.

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream: stream, bufferSize: 1024, leaveOpen:true, hasHeader: true);

In addition to asynchronous methods, there are also synchronous methods.

    toDoes.WriteCsv(csvFileName);

NuGet and GitHub

These libraries are open to NuGet and can be installed from Visual Studio.

Source code is available at:

The projects included are:

Shos.CsvHelper

  • CSV library
  • .NET Standard Library version
  • You can build a.NET Standard 1.3 and later
  • .NET Network 4.6 or higher, or.NET Core 1.1 or later for
  • NuGet packages that can be installed: NuGet Gallery | Shos.CsvHelper

Shos.CsvHelper.NetFramework

Shos.CsvHelperSample.NetCore

  • .NET Core Console applications that use the Shos.CsvHelper sample

Shos.CsvHelperSample.NetFramework

  • Using Shos.CsvHelper.NetFramework.NET Framework console application sample