ovska / FlameCsv

High performance RFC 4180 compliant CSV parsing library for .NET 6+
MIT License
6 stars 1 forks source link
csharp csv data-structures dotnet performance

FlameCsv

High-performance RFC 4180-compliant CSV library for .NET 8+ with trimming/AOT support

Features

Examples

Reading records

string data = "id,name,lastlogin\n1,Bob,2010-01-01\n2,Alice,2024-05-22";

foreach (var user in CsvReader.Read<User>(data))
{
    Console.WriteLine(user);
}

record User(int Id, string Name, DateTime LastLogin, int? Age = null);

Reading headerless CSV

string data = "1,Bob,2010-01-01\n2,Alice,2024-05-22";
var options = new CsvTextOptions { HasHeader = false };

foreach (var user in CsvReader.Read<User>(data, options))
{
    Console.WriteLine(user);
}

class User
{
    [CsvIndex(0)] public int Id { get; set; }
    [CsvIndex(1)] public string? Name { get; set; }
    [CsvIndex(2)] public DateTime LastLogin { get; set; }
}

Reading UTF8 directly from bytes

var options = new CsvUtf8Options { /* configure here */ };
await foreach (var user in CsvReader.ReadAsync<User>(File.OpenRead(@"C:\test.csv"), options))
{
    Console.WriteLine(user);
}

Source gen (NativeAOT/trimming)

foreach (var user in CsvReader.Read<User>(data, UserTypeMap.Instance))
{
    Console.WriteLine(user);
}

record User(int Id, string Name, DateTime LastLogin, int? Age = null);

[CsvTypeMap<char, User>]
partial class UserTypeMap;

Reading fields manually

string data = "id,name,lastlogin,age\n1,Bob,2010-01-01,42\n2,Alice,2024-05-22,\n";

// case insensitive header names (enabled by default)
var options = new CsvTextOptions { Comparer = StringComparer.OrdinalIgnoreCase };

foreach (CsvValueRecord<char> record in CsvReader.Enumerate(data, options))
{
    // get fields by column index of header name
    var u1 = new User(
        Id:        record.GetField<int>(0),
        Name:      record.GetField<string>(1),
        LastLogin: record.GetField<DateTime>(2),
        Age:       record.GetFieldCount() >= 3 ? record.GetField<int?>(3) : null);

    var u2 = new User(
        Id:        record.GetField<int>("Id"),
        Name:      record.GetField<string>("Name"),
        LastLogin: record.GetField<DateTime>("LastLogin"),
        Age:       record.GetFieldCount() >= 3 ? record.GetField<int?>("Age") : null);
}

Copy CSV structure into objects for later use

The CsvValueRecord<T> struct wraps around buffers from the CSV data source directly. To access the records safely outside a foreach, use AsEnumerable() or manually call new CsvRecord<T>(csvValueRecord).

string data = "id,name,lastlogin,age\n1,Bob,2010-01-01,42\n2,Alice,2024-05-22,\n";

// CsvRecord reference type copies the CSV fields for later use
List<CsvRecord<char>> records = [.. CsvReader.Enumerate(data).AsEnumerable()];
Console.WriteLine("First name: " + records[0].GetField(1));

Writing records

User[] data =
[
    new User(1, "Bob", DateTime.UnixEpoch, 42),
    new User(2, "Alice", DateTime.UnixEpoch, null),
];

StringBuilder result = CsvWriter.WriteToString(data);
Console.WriteLine(result);

record User(int Id, string Name, DateTime LastLogin, int? Age = null);

Writing fields manually

var output = new MemoryStream();
await using (var writer = CsvWriter.Create(output))
{
    writer.WriteRaw("id,name,lastlogin"u8);
    writer.NextRecord();

    writer.WriteField(1);
    writer.WriteField("Bob");
    writer.WriteField(DateTime.UnixEpoch);
    writer.WriteField(42);
    writer.NextRecord();

    writer.WriteField(2);
    writer.WriteField("Alice");
    writer.WriteField(DateTime.UnixEpoch);
    writer.WriteField(ReadOnlySpan<char>.Empty, skipEscaping: true);
    writer.NextRecord();
}

Console.WriteLine(Encoding.UTF8.GetString(output.ToArray()));

record User(int Id, string Name, DateTime LastLogin, int? Age = null);

Writing records manually

var output = new StringWriter();
using var writer = CsvWriter.Create(output);

User[] data =
[
    new User(1, "Bob", DateTime.UnixEpoch, 42),
    new User(2, "Alice", DateTime.UnixEpoch, null),
];

writer.WriteHeader<User>();

foreach (User item in data)
{
    writer.WriteRecord<User>(item);
}

writer.Flush();

Console.WriteLine(output.ToString());

record User(int Id, string Name, DateTime LastLogin, int? Age = null);

Performance

Note: CsvHelper was chosen because it is the most popular CSV library for C#. This library isn't meant to be a replacement for CsvHelper. Example CSV file used in th benchmarks is found in the TestData folder in the tests-project.


image

image


image

image

--

image

image