Fork me on GitHub

Reading

This library was created to work as easy as possible without any configuration by default. If your class property names match your CSV file header names, it's as simple as this.

var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>();

Getting All Records


The most common scenario is using one of the GetRecords methods. You can specify the type of object you want returned, and it will return an IEnumerable that will yield results. This means when iterating the results, only a single record will be in memory at a time, instead of the entire file. Records aren't pulled until you actually start iterating the results.

If you want to use an anonymous type as your record, you can get the records by supplying a type definition. You can use default( type ) for value types, and new for reference types.

GetRecords

Returns an IEnumerable<T> of records.

// By type
var records = csv.GetRecords<MyClass>();
var records = csv.GetRecords( typeof( MyClass ) );

// Dynamic
var records = csv.GetRecords<dynamic>();

// Using anonymous type for the class definition
var anonymousTypeDefinition =
{
    Id = default( int ),
    Name = string.Empty,
    MyClass = new MyClass()
};
var records = csv.GetRecords( anonymousTypeDefinition );

EnumerateRecords

You can also enumerate the records while re-using a class instance that you give. Each enumeration will hydrate the given record, but only the mapped members. If you supplied a map and didn't map one of the members, that member will not get hydrated with the current row's data. Be careful. Any methods that you call on the projection that force the evaluation of the IEnumerable, such as ToList(), you will get a list where all the records are the same instance you provided that is hydrated with the last record in the CSV file.

var record = new MyClass();
var records = csv.EnumerateRecords( record );
foreach( var r in records )
{
    // r is the same instance as record.
}

Reading Records


To be able to get individual records or even fields, you need to iterate through the records. This is done using the Read methods. Read will advance the reader to the next record. You must call Read before you can get any records or fields. When GetRecords, Read is automatically called for you.

Read

This will advance the reader to the next record.

csv.Read();

ReadAsync

This will advance the reader to the next record asynchronously. If the TextReader that was supplied is tied to a network or some other slow to read functionality, reading asynchronously is probably a good idea.

await csv.ReadAsync();

ReadHeader

The header in a CSV file is just another record, but it has special meaning. If your file has a header record, you'll need to read the header after the first read. After that you can loop the records and read them. This will allow you to be able to read headers on different rows, or even multiple headers.

csv.Read();
csv.ReadHeader();
while( csv.Read() )
{
    var record = csv.GetRecord<MyClass>();
}

Getting a Single Record


Sometimes there is a reason that you may need to loop the records yourself. You can still easily get a single record, just like with multiple records.

GetRecord

// Don't forget to read the data before getting it.
csv.Read();

// By type
var record = csv.GetRecord<MyClass>();
var record = csv.GetRecord( typeof( MyClass ) );

// Dynamic
var record = csv.GetRecord<dynamic>();

// Using anonymous type for the class definition
var anonymousTypeDefinition =
{
    Id = default( int ),
    Name = string.Empty,
    MyClass = new MyClass()
};
var record = csv.GetRecord( anonymousTypeDefinition );

Getting Fields


If you need a more granular way of getting records, you are able to get individual fields.

Indexer

You can use an indexer to get by position or name. This will return the field as a string.

// Don't forget to read the data before getting it.
csv.Read();

// By position
var field = csv[0];

// By header name
var field = csv["HeaderName"];

GetField

You can get fields converted to a specific type.

// Don't forget to read the data before getting it.
csv.Read();

// Gets field by position returning string
var field = csv.GetField( 0 );

// Gets field by position returning int
var field = csv.GetField<int>( 0 );

// Gets field by header name returning bool
var field = csv.GetField<bool>( "IsTrue" );

// Gets field by header name returning object
var field = csv.GetField( typeof( bool ), "IsTrue" );

TryGetField

If there data isn't consistent and sometimes may not be able to be converted to the correct type, you can use the TryGetField methods.

// Don't forget to read the data before getting it.
csv.Read();

var success = csv.TryGetField<int>( 0, out string field );

Malformed Field Fallback


If the field is malformed, meaning it doesn't follow RFC 4180, some fallback strategies are used so the fields can still be read. In general, this is how a malformed file would behave when opened in Excel.

  1. \r or \n is used instead of \r\n
    • Both \r and \n are handled as a line ending, just like \r\n.
  2. No \r\n at the end of the file
    • The last row and field is read as if there was a \r\n.
  3. Escaped field has space before first quote.
    • The field is treated as a non escaped field.
    • , "field", -> ][ "field"][
  4. Escaped field has characters after second quote.
    • Characters after second quote aren't modified.
    • ,"field" , -> ][field ][
    • ,"field" "s, -> ][field "s][
    • ,"field "" s", -> ][field " s"][
  5. Escaped field has no ending quote.
    • The field will go to the end of the file.
    • a,b,"c\r\nd,e,f\r\n -> [a][b][c\r\nd,e,f\r\n]

Reading Context


When reading, all the information in the system is held in a context object. If you need to get raw system information for some reason, it's available here. When an exception is thrown, the context is included so you can inspect the current state of the reader.

Configuration