![]() |
VOOZH | about |
dotnet add package TinyCsvParser --version 3.0.11
NuGet\Install-Package TinyCsvParser -Version 3.0.11
<PackageReference Include="TinyCsvParser" Version="3.0.11" />
<PackageVersion Include="TinyCsvParser" Version="3.0.11" />Directory.Packages.props
<PackageReference Include="TinyCsvParser" />Project file
paket add TinyCsvParser --version 3.0.11
#r "nuget: TinyCsvParser, 3.0.11"
#:package TinyCsvParser@3.0.11
#addin nuget:?package=TinyCsvParser&version=3.0.11Install as a Cake Addin
#tool nuget:?package=TinyCsvParser&version=3.0.11Install as a Cake Tool
👁 NuGet Package
👁 Build status
👁 License: MIT
TinyCsvParser is a high-performance CSV parsing library for .NET. This documentation explains the usage, configuration, and extensibility of the library through practical examples.
Upgrading from a previous version? Check out the Migration Guide from 2.x to 3.x
To include TinyCsvParser in your project, install the NuGet package using the .NET CLI:
dotnet add package TinyCsvParser
Alternatively, you can use the NuGet Package Manager in Visual Studio:
Install-Package TinyCsvParser
To parse a CSV file, you need a target model and a mapping definition.
The target of your parsing operation should be a class with a parameterless constructor.
public class Person
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
Create a class inheriting from CsvMapping<T> and define the relationship between CSV columns and model properties.
This approach is flexible as it doesn't depend on the order of columns in the CSV file. The parser automatically resolves the names to indices.
public class PersonMapping : CsvMapping<Person>
{
public PersonMapping()
{
MapProperty("ID", x => x.Id);
MapProperty("Full Name", x => x.Name);
}
}
Use this for files without headers or for maximum performance.
public class PersonMappingByIndex : CsvMapping<Person>
{
public PersonMappingByIndex()
{
// 0-based index: ID is column 0, Name is column 1
MapProperty(0, x => x.Id);
MapProperty(1, x => x.Name);
}
}
TinyCsvParser automatically handles fields wrapped in quotes. This is essential when your data or your header names contain the delimiter character or line breaks.
// Example CSV: "ID";"Full Name"
// The parser strips the quotes automatically.
// You map using the clean name:
MapProperty("Full Name", x => x.Name);
Quoted fields can contain the delimiter (e.g., "Doe, John") or even escaped quotes (e.g., "The ""Great"" Gatsby"), which the parser resolves before passing the value to the mapping.
The CsvParser is the central engine. It is stateless and can be reused for multiple parsing operations.
First, you combine your CsvOptions and your CsvMapping to create the parser instance.
// 1. Define the technical format
CsvOptions options = new(
Delimiter: ';',
QuoteChar: '"',
EscapeChar: '"',
SkipHeader: true,
CommentCharacter: '#'
);
// 2. Instantiate your mapping logic
PersonMapping mapping = new();
// 3. Create the parser (Stateless and reusable)
CsvParser<Person> parser = new(options, mapping);
You can read the CSV data synchronously or asynchronously. TinyCsvParser provides full support for IAsyncEnumerable
for maximum
performance with asynchronous data streams. Crucially, the parsing process uses deferred execution (lazy loading). The
file is
read and parsed one record at a time as you iterate.
The parser supports reading from strings, streams, or files.
// Calling ReadFromFile does NOT start the parsing yet.
// It returns an Enumerable that waits for a foreach loop.
IEnumerable<CsvMappingResult<Person>> results = parser.ReadFromFile("data.csv");
// The actual parsing happens here, one record at a time.
foreach (CsvMappingResult<Person> result in results)
{
// Every result encapsulates Success, Error, or Comment states.
if (result.IsSuccess)
{
Person person = result.Result;
Console.WriteLine($"Parsed: {person.Name}");
}
}
For modern applications, ReadFromFileAsync can be used to minimize buffer copying and memory pressure.
// Returns an IAsyncEnumerable
var resultsAsync = parser.ReadFromFileAsync("data.csv");
// Iterate asynchronously using await foreach
await foreach (var result in resultsAsync.ConfigureAwait(false))
{
if (result.IsSuccess)
{
Person person = result.Result;
Console.WriteLine($"Async Parsed: {person.Name}");
}
}
TinyCsvParser distinguishes between two types of indices. This distinction is necessary because CSV files often deviate from a simple "one line equals one record" structure.
LineNumber: Refers to the physical line in the source file (1-based).RecordIndex: Refers to the logical data entity (0-based).Usage Tip: Always use LineNumber when reporting errors to users, as it corresponds directly to what they see in a text editor!
The CsvMappingResult<T> captures every possible state of a row. The Switch method ensures
all states are handled correctly.
foreach (CsvMappingResult<Person> item in parser.ReadFromStream(stream))
{
item.Switch(
onSuccess: (Person entity) =>
Console.WriteLine($"[Record {item.RecordIndex}] Imported: {entity.Name}"),
onFailure: (CsvMappingError error) =>
Console.WriteLine($"[Line {item.LineNumber}] Error in Column {error.ColumnIndex}: {error.Value}"),
onComment: (string comment) =>
Console.WriteLine($"[Line {item.LineNumber}] Meta-Info: {comment}")
);
}
When the CSV schema is only known at runtime, or you want to avoid creating dedicated classes for
simple scripts, you can parse rows directly into dynamic structures (Dictionary<string, object?> or
ExpandoObject).
For performance it's maybe better to map to a Dictionary, as it avoids Dynamic Language Runtime overhead.
Use the static factory methods on the CsvParser class. The schema is configured inline using a delegate.
using TinyCsvParser;
CsvOptions options = new(Delimiter: ';', QuoteChar: '"', EscapeChar: '"', SkipHeader: false);
// Create the parser and configure the schema in one go
var parser = CsvParser.CreateDictionaryParser(options, schema =>
{
schema.Add<int>("Id"); // Resolves Int32Converter automatically
schema.Add<double>("Price"); // Resolves DoubleConverter automatically
});
foreach (var result in parser.ReadFromFile("products.csv"))
{
if (result.IsSuccess)
{
Dictionary<string, object?> row = result.Result;
Console.WriteLine($"Item {row["Id"]} costs {row["Price"]}");
}
}
Note: Use
CsvParser.CreateExpandoParser(...)if you prefer to access fields via the dynamic keyword likerow.Id.
While Add<T> is the most convenient method, you can pass explicit converter instances if you need special
configurations
(e.g., date formats).
var parser = CsvParser.CreateDictionaryParser(options, schema =>
{
schema.Add<int>("Id");
schema.Add("BirthDate", new DateTimeConverter("yyyy-MM-dd")); // Explicit Converter
});
Any column present in the CSV header that is not mapped in your CsvSchema will automatically be
parsed as a raw string. This prevents data loss while maintaining strict typing for the columns
you care about.
For complex logic, MapUsing provides direct access to the ref struct CsvRow. To ensure errors are handled
properly, the delegate returns a MapUsingResult.
public class AdvancedMapping : CsvMapping<Person>
{
public AdvancedMapping()
{
MapUsing((Person entity, ref CsvRow row) =>
{
if (row.Count < 2)
return MapUsingResult.Failure("Too few columns.");
if (!int.TryParse(row.GetSpan(0), out int id))
return MapUsingResult.Failure($"Invalid ID: {row.GetString(0)}");
entity.Id = id;
entity.Name = row.GetString(1);
return MapUsingResult.Success();
});
}
}
You can pass specific parameters (like date formats) to built-in converters during mapping.
DateTimeConverter dateConverter = new("yyyy-MM-dd");
MapProperty("BirthDate", x => x.BirthDate, dateConverter);
Inherit from NonNullableConverter<T> to implement custom parsing logic directly on the memory spans.
public class YesNoConverter : NonNullableConverter<bool>
{
protected override bool InternalConvert(ReadOnlySpan<char> value, out bool result)
{
if (value.Equals("Yes".AsSpan(), StringComparison.OrdinalIgnoreCase))
{
result = true;
return true;
}
result = false;
return false;
}
}
In Version 2.x, custom logic used a string[]. In Version 3.0, it uses ref CsvRow. This allows the library to work
with ReadOnlySpan<char>, significantly reducing memory allocations.
Error objects in Version 3.0 now contain both RecordIndex and LineNumber. If you previously relied on indices for
debugging, ensure
you switch to LineNumber for file-based troubleshooting. This is what the user sees in their CSV file.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 5 NuGet packages that depend on TinyCsvParser:
| Package | Downloads |
|---|---|
|
Dapplo.Config
Dapplo.Config provides an easy to use configuration |
|
|
Sportradar.MTS.SDK
The SDK simplifying the integration with Sportradar's MTS services |
|
|
Sportradar.MTS.SDKCore
The SDK simplifying the integration with Sportradar's MTS services (.NET Standard 2.1) |
|
|
Coder.Helper
开发助手类,包含类型转换,Json,Xml,Encrypt,Http,NPOI,File,Regex,Serialize,Email,雪花id,汉字转拼音等助手类。 |
|
|
Traderr.Services
Traderr.io |
Showing the top 6 popular GitHub repositories that depend on TinyCsvParser:
| Repository | Stars |
|---|---|
|
TheAxelander/OpenBudgeteer
OpenBudgeteer is a budgeting app based on the Bucket Budgeting Principle
|
|
|
bing-framework/Bing.NetCore
Bing是基于 .net core 3.1 的框架,旨在提升团队的开发输出能力,由常用公共操作类(工具类、帮助类)、分层架构基类,第三方组件封装,第三方业务接口封装等组成。
|
|
|
unfrl/dug
A global DNS propagation checker that gives pretty output. Written in dotnet core
|
|
|
leandromoh/RecordParser
Zero Allocation Writer/Reader Parser for .NET Core
|
|
|
jsakamoto/Toolbelt.Blazor.I18nText
The class library that provides the ability to localize texts on your Blazor app!
|
|
|
MalukuSeito/Character-Builder-5
A Character Builder for 5e
|
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.11 | 11,239 | 5/16/2026 |
| 3.0.10 | 17,433 | 4/3/2026 |
| 3.0.9 | 168 | 4/3/2026 |
| 3.0.8 | 185 | 4/3/2026 |
| 3.0.7 | 2,583 | 3/25/2026 |
| 3.0.6 | 2,882 | 3/23/2026 |
| 3.0.4 | 301 | 3/21/2026 |
| 3.0.3 | 275 | 3/20/2026 |
| 3.0.2 | 1,365 | 3/19/2026 |
| 3.0.1 | 12,688 | 2/24/2026 |
| 3.0.0 | 2,303 | 2/20/2026 |
| 2.7.2 | 63,421 | 1/18/2026 |
| 2.7.1 | 636,304 | 12/14/2024 |
| 2.7.0 | 2,310,621 | 6/3/2022 |
| 2.6.2 | 13,053 | 5/31/2022 |
| 2.6.1 | 1,511,573 | 6/17/2021 |
| 2.6.0 | 693,755 | 10/31/2020 |
| 2.5.2 | 2,197,728 | 7/14/2019 |
| 2.5.1 | 467,041 | 5/30/2019 |
| 2.5.0 | 2,299 | 5/26/2019 |