Read CSV files in C# .NET Core

Last updated: October 28, 2026 · Tested on .NET 8 and .NET 10 with CsvHelper 34.0.0 and TinyCsvParser 3.0.11

Quick answer: to read a CSV file in C#, use a CSV parser rather than string.Split(','). The three easiest options in .NET Core and .NET 8+ are CsvHelper (most popular, maps by header name), TinyCsvParser (fast, reports bad rows without crashing) and TextFieldParser (built into .NET, no NuGet package needed).

This guide reads the same CSV file into a typed C# class with each parser, with complete code you can copy. Every example was compiled and run before publishing. At the end you’ll find a comparison table, a CSV-to-JSON example and answers to common questions.

Prerequisites

You need the .NET 8 SDK or later. The TextFieldParser example also runs on older .NET Core 3.1 projects. Create a console app to follow along:

dotnet new console -n CsvReaderDemo
cd CsvReaderDemo

Sample CSV file and model

All three examples read the same users.csv, which has a header row and one value that contains a comma inside quotes:

Id,Name,City,Country
1,Anna Martin,Toronto,Canada
2,Lucas Bernard,Paris,France
3,Priya Shah,"Mumbai, Maharashtra",India
Sample users.csv file opened in Excel with Id, Name, City and Country columns

Each row maps to this model class (CsvHelper needs two small additions, shown in its section):

public class UserDetails
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string City { get; set; } = "";
    public string Country { get; set; } = "";
}

In the project file, set the CSV to copy to the output folder so the app can find it at run time:

<ItemGroup>
  <None Update="users.csv" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

Approach 1: Read CSV with TinyCsvParser

TinyCsvParser is a small, high-performance CSV library. Each row comes back as either a parsed object or an error with its line number, so one bad row never stops the whole import. Install it:

dotnet add package TinyCsvParser

Create a mapping class that links CSV header names to properties (you can also map by column index with MapProperty(0, x => x.Id)):

using TinyCsvParser;

public class UserDetailsMapping : CsvMapping<UserDetails>
{
    public UserDetailsMapping()
    {
        MapProperty("Id", x => x.Id);
        MapProperty("Name", x => x.Name);
        MapProperty("City", x => x.City);
        MapProperty("Country", x => x.Country);
    }
}

Then set the CSV options, create the parser and read the file:

using System.Text;
using TinyCsvParser;
using TinyCsvParser.Models;

var options = new CsvOptions(
    Delimiter: ',',
    QuoteChar: '"',
    EscapeChar: '"',
    Encoding: Encoding.UTF8,
    SkipHeader: true);

var parser = new CsvParser<UserDetails>(options, new UserDetailsMapping());

foreach (var row in parser.ReadFromFile("users.csv"))
{
    if (row.IsSuccess)
    {
        var user = row.Result;
        Console.WriteLine($"{user.Id}: {user.Name}, {user.City}, {user.Country}");
    }
    else if (row.IsError)
    {
        Console.WriteLine($"Line {row.LineNumber}: {row.Error}");
    }
}

Output:

Console output of reading a CSV file in C# .NET: 1: Anna Martin, Toronto, Canada; 2: Lucas Bernard, Paris, France; 3: Priya Shah, Mumbai, Maharashtra, India

If a row has a value that can’t be converted, such as abc in the Id column, you get a message like Line 5: Error at Column 0: Conversion failed: abc and the parser moves on to the next row. Parsing is lazy: rows are read one at a time as you loop, so large files don’t fill memory. Use ReadFromFileAsync with await foreach in async code.

Upgrading from TinyCsvParser 2.x? Version 3 replaced CsvParserOptions with CsvOptions, renamed IsValid to IsSuccess, and reports errors with a LineNumber. Older tutorials that use new CsvParserOptions(true, ',') won’t compile against the current package.

Approach 2: Read CSV with CsvHelper

CsvHelper is the most widely used CSV library for .NET. It matches columns to properties by header name, so the column order in the file doesn’t matter, and it can write CSV too. Install it:

dotnet add package CsvHelper

CsvHelper builds the mapping code at compile time. Mark the model with [CsvRecord] and make it partial:

using CsvHelper;

[CsvRecord]
public partial class UserDetails
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string City { get; set; } = "";
    public string Country { get; set; } = "";
}

Then read the file:

using CsvHelper;

using var reader = CsvReader.FromFile("users.csv");

foreach (UserDetails user in reader.GetRecords<UserDetails>())
{
    Console.WriteLine($"{user.Id}: {user.Name}, {user.City}, {user.Country}");
}

GetRecords streams rows one at a time, so memory stays flat even for very large files. Add .ToList() only when you need every row in memory at once.

When CSV headers don’t match your property names

Use [CsvColumn] to point a property at a different header. For a file with the headers UserId,Full Name,City,Country:

[CsvRecord]
public partial class UserRow
{
    [CsvColumn("UserId")]
    public int Id { get; set; }

    [CsvColumn("Full Name")]
    public string Name { get; set; } = "";

    public string City { get; set; } = "";
    public string Country { get; set; } = "";
}

Reading individual fields

If you don’t want a class at all, read field by field:

using var reader = CsvReader.FromFile("users.csv");

while (reader.Read())
{
    int id = reader.Parse<int>("Id");
    string name = reader.GetString("Name");
    string city = reader.GetString("City");
    Console.WriteLine($"{id}: {name} ({city})");
}

Using code from an older CsvHelper tutorial? CsvHelper 34 is a rewrite. new CsvReader(reader, CultureInfo.InvariantCulture), CsvConfiguration and ClassMap<T> are the version 33 API and no longer compile. See the official CsvHelper 34 migration guide for before-and-after examples.

For more options, such as writing CSV files, see our complete CsvHelper guide.

Approach 3: Read CSV with TextFieldParser (no NuGet package)

TextFieldParser ships with .NET Core 3.0 and later in the Microsoft.VisualBasic.FileIO namespace, so you don’t need any NuGet package. You can use it from C# like any other class. It handles quoted values correctly, but it returns each row as a string[], so you map fields to your class yourself:

using Microsoft.VisualBasic.FileIO;

var users = new List<UserDetails>();

using var parser = new TextFieldParser("users.csv");
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
parser.HasFieldsEnclosedInQuotes = true;

parser.ReadLine(); // skip the header row

while (!parser.EndOfData)
{
    string[] fields = parser.ReadFields() ?? Array.Empty<string>();

    users.Add(new UserDetails
    {
        Id = int.Parse(fields[0]),
        Name = fields[1],
        City = fields[2],
        Country = fields[3]
    });
}

foreach (var user in users)
{
    Console.WriteLine($"{user.Id}: {user.Name}, {user.City}, {user.Country}");
}

With HasFieldsEnclosedInQuotes set, the value "Mumbai, Maharashtra" comes back as one field instead of being split in two. If a line is badly formed, ReadFields() throws a MalformedLineException, which includes the line number so you can log it and move on.

For a deeper walkthrough, see our TextFieldParser guide.

Common pitfalls with any approach

  • Splitting on commas yourself. line.Split(',') breaks on quoted values like "Mumbai, Maharashtra". Use a parser.
  • Encoding. Files saved from Excel are often UTF-8 with a byte-order mark, or Windows-1252. Pass the encoding explicitly if names with accents come out garbled.
  • Header row. Skip it explicitly (TinyCsvParser, TextFieldParser) or let CsvHelper use it for mapping. Don’t parse it as data.
  • Large files. Stream rows instead of loading everything with ToList() or File.ReadAllLines.

CsvHelper vs TinyCsvParser vs TextFieldParser: which should you use?

For most projects, start with CsvHelper: it maps by header name, streams large files and also writes CSV. Choose TinyCsvParser when you need a clear error for every bad row, and TextFieldParser when you can’t add a NuGet package.

ApproachNuGet packageMaps columns byReturnsBest for
CsvHelperCsvHelperHeader name (or [CsvColumn])Typed objectsMost apps, and anything that also writes CSV
TinyCsvParserTinyCsvParserHeader name or indexTyped objects + per-row errors with line numbersImports where bad rows must be reported, not crash the run
TextFieldParserNone (built in)Manualstring[] per rowSmall tools with no extra dependencies

Bonus: convert the CSV to JSON

Free and easy tool :

Doest store data to cloud. Your information remains with you.

Using Code

Once the rows are in a List<UserDetails>, the built-in System.Text.Json turns them into JSON in one call:

using System.Text.Json;

string json = JsonSerializer.Serialize(users, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("users.json", json);

Frequently asked questions

How do I read a CSV file in C# without a library?

Use TextFieldParser from the Microsoft.VisualBasic.FileIO namespace. It’s built into .NET Core 3.0 and later and handles quoted values. Avoid File.ReadAllLines with Split(','), which breaks on commas inside quotes.

How do I skip the header row?

With TinyCsvParser, set SkipHeader: true in CsvOptions. With TextFieldParser, call parser.ReadLine() once before the loop. CsvHelper reads the header automatically and uses it to match columns.

How do I read a large CSV file efficiently?

Loop over the rows instead of loading them all at once. CsvHelper’s GetRecords and TinyCsvParser’s ReadFromFile both read one row at a time, so memory use stays low however big the file is.

How do I read a CSV file with a semicolon or tab separator?

In TinyCsvParser, set Delimiter: ';' (or '\t') in CsvOptions. In TextFieldParser, call parser.SetDelimiters(";"). In CsvHelper, pass new CsvReaderOptions { Separator = ';' } to CsvReader.FromFile.

Summary

All three approaches read the same CSV file into the same typed list. Don’t split lines on commas yourself, pick a parser that handles quoted values, and stream rows when files get large. Every example in this article was compiled and run before publishing (.NET 8 and .NET 10).

References

4 thoughts on “Read CSV Files in C# .NET Core and .NET 8+: 3 Easy Ways with Examples”

Leave a Reply

Your email address will not be published. Required fields are marked *