FlatFiles
Reads and writes CSV, fixed-length and other flat file formats with a focus on schema definition, configuration and speed.
Install / Use
/learn @jehugaleahsa/FlatFilesREADME
Archived 2026-03-28: I maintained this project for nearly 10 years. If you are stumbling upon this just now, you'll see it targets versions of .NET that MS hasn't supported in a long, long time. Originally, I was very motivated to provide a CSV library that had a clean separation between schema definition and the actual record parsing. However, alternative CSV libraries continued to evolve and, with the advent of
Span<T>andMemory<T>, I lacked the motivation to upgrade my implementation to take advantage of them. Soon, there was limited competitive advantage to this library over others.Furthermore, should I ever revisit these shores again, I would re-envision it entirely. Rather than write my own CSV and flat file parsers, I would just provide the schema definition capabilities and parse the raw output (i.e.,
strings) of existing libraries. I think that's a cleaner separation of concerns.
FlatFiles
Reads and writes CSV, fixed-length and other flat file formats with a focus on schema definition, configuration and speed. Supports mapping directly between files and classes.
Download using NuGet: FlatFiles
You can check out all of the awesome enhancements and new features in the CHANGELOG.
Overview
Plain-text formats primarily come in two variations: delimited (CSV, TSV, etc.) and fixed-width. FlatFiles comes with support for working with both formats. Unlike most other libraries, FlatFiles puts a focus on schema definition. You build and pass a schema to a reader or writer and it will use the schema to extract or write out your values.
A schema is defined by specifying what data columns are in your file. A column has a name, a type and an ordinal position in the file. The order matches whatever order you add the columns to the schema, so you're left just specifying the name and the type. Beyond that, you have a lot of control over the parsing/formatting behavior when reading and writing, respectively. Most of the time, the out-of-the-box options will just work, too. But when you need that level of extra control, you don't have to bend over backward to work around the API, like with many other libraries. FlatFiles was designed to make handling oddball edge cases easier.
If you are working with data classes, defining schemas is even easier. You can use the type mappers to map your properties directly. This saves you from having to specify column names or types, since both can be derived from the property. For those working with ADO.NET, there's even support for DataTables and IDataReader. If you really want to, you can read and write values using raw object[].
Table of Contents
- Overview
- Type Mappers
- Schemas
- Delimited Files
- Fixed Length Files
- Handling Nulls
- Ignored Fields
- Metadata
- Skipping Records
- Error Handling
- Files Containing Multiple Schemas
- Custom Mapping
- Runtime Mapping
- Disabling Optimization
- Non-Public Classes and Members
- ADO.NET DataTables
- FlatFileDataReader
- License
Type Mappers
Using the type mappers, you can directly read file contents into your classes:
customer_id,name,created,avg_sales
1,bob,20120321,12.34
2,Susan,20130108,13.88
3,Tom,20180519,88.23
var mapper = DelimitedTypeMapper.Define<Customer>();
mapper.Property(c => c.CustomerId).ColumnName("customer_id");
mapper.Property(c => c.Name).ColumnName("name");
mapper.Property(c => c.Created).ColumnName("created").InputFormat("yyyyMMdd");
mapper.Property(c => c.AverageSales).ColumnName("avg_sales");
using (var reader = new StreamReader(File.OpenRead(@"C:\path\to\file.csv")))
{
var options = new DelimitedOptions() { IsFirstRecordSchema = true };
var customers = mapper.Read(reader, options).ToList();
}
To define the schema when working with type mappers, call Property in the order that the fields appear in the file. The type of the column is determined by the type of the mapped property. Each property configuration provides options for controlling the way FlatFiles handles strings, numbers, date/times, GUIDs, enums and more. Once the properties are configured, you can call Read or Write on the type mapper.
Note The Read method only retrieves records from the underlying file on-demand. To bring the entire file into memory at once, just call ToList or ToArray, or loop over the records inside of a foreach. This is good news for people working with enormous files!
Writing to a file is just as easily:
mapper.Property(c => c.Created).OutputFormat("yyyyMMdd");
mapper.Property(c => c.AverageSales).OutputFormat("N2");
using (var writer = new StreamWriter(File.OpenCreate(@"C:\path\to\file2.csv")))
{
var options = new DelimitedOptions() { IsFirstRecordSchema = true };
mapper.Write(writer, customers, options);
}
Note I was able to customize the OutputFormat of properties that were previously configured. The first time Property is called on a property, FlatFiles assumes it's the next column to appear in the flat file. However, subsequent configuration on the property doesn't change the order of the columns or reset any other settings.
Auto-mapping
If your delimited file (CSV, TSV, etc.) has a schema with column names that match your class's property names, you can use the GetAutoMappedReader method as a shortcut. This method will read the schema from your file and map the columns to the properties automatically, returning a reader for retrieving the data. It's important to note that you cannot customize the parsing behavior of any of the columns, at which point you are better off explicitly defining the schema. Fortunately, FlatFiles uses pretty liberal parsing out-of-the-box, so most common formats will work.
By default, columns and properties are matched by name (case-insensitive). If you need more control over how columns and properties are matched, you can pass in your own IAutoMapMatcher. Given an IColumnDefinition and a MemberInfo, a matcher must determine whether the two map to one another. For convenience, you can also use the AutoMapMatcher.For method to pass a Func<IColumnDefinition, MemberInfo, bool> delegate rather than implement the interface.
Similarly, use the GetAutoMappedWriter method to automatically write out a delimited file. Note that there's no way to control the column formatting. However, you can control the name and position of the columns by passing an IAutoMapResolver. The IAutoMapResolver interface provides the GetPosition and a GetColumnName methods, both accepting a MemberInfo. For convenience, you can also use the AutoMapResolver.For method to pass delegates for determining the names/positions, rather than implement the interface.
Schemas
Under the hood, type mapping internally defines a schema, giving each column a name, order and type in the flat file. You can get access to the schema by calling GetSchema on the mapper.
You can work directly with schemas if you don't plan on using the type mappers. For instance, this is how we would define a CSV file schema:
var schema = new DelimitedSchema();
schema.AddColumn(new Int64Column("customer_id"))
.AddColumn(new StringColumn("name"))
.AddColumn(new DateTimeColumn("created") { InputFormat = "yyyyMMdd", OutputFormat = "yyyyMMdd" })
.AddColumn(new DoubleColumn("avg_sales") { OutputFormat = "N2" });
Or, if the schema is for a fixed-length file:
var schema = new FixedLengthSchema();
schema.AddColumn(new Int64Column("customer_id"), 10)
.AddColumn(new StringColumn("name"), 255)
.AddColumn(new DateTimeColumn("created") { InputFormat = "yyyyMMdd", OutputFormat = "yyyyMMdd" }, 8)
.AddColumn(new DoubleColumn("avg_sales") { OutputFormat = "N2" }, 10);
The FixedLengthSchema class is the same as the DelimitedSchema class, except it associates a Window to each column. A Window records the Width of the column in the file. It also allows you to specify the Alignment (left or right) in cases where the value doesn't fill the entire width of the column (the default is left aligned). The FillCharacter property can be used to say what character is used as padding. You can also set the TruncationPolicy to say whether to chop off the front or the back of values that exceed their width.
Note Some fixed-length files may have columns that are not used. The fixed-length schema doesn't provide a way to specify a starting index for a column. Look at the Ignored Fields section below to learn about ways to to handle this.
Delimited Files
If you are working with delimited files, such as comma-separated (CSV) or tab-separated (TSV) files, you want to use the DelimitedTypeMapper. Internally, the mapper uses the DelimitedReader and DelimitedWriter classes, both of which work in terms of raw object arrays. In effect, all the mapper does is map the values in the array to the properties in your data objects. These classes read data from a TextReader, such as a StreamReader or a StringReader, and write data to a TextWriter, such as a StreamWriter or a StringWriter. Internally, the mapper will bui
Related Skills
node-connect
349.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
