A forward-only CSV reader. Input is parsed as UTF-8 bytes with SIMD instructions; fields are exposed as spans into the read buffer and are only materialized as strings or numbers when asked for.
Spans returned for a record are valid until the next call to Read or ReadAsync.
A reader is not thread-safe: use it from one thread at a time, and finish each async call before starting the next. Nothing checks this; two threads reading one reader get wrong records, not an exception. To map records on several threads, use ParallelEnumerate<T>(Func<CsvReader, T>, int). The options, the record mappers and the CsvDynamicRecords a reader returns do not change, so they can be shared between threads.
The offset, in bytes of UTF-8 input, where the current record starts, counted from the start of the input (a byte order mark included), for progress reports and error messages. UTF-16 input (strings, text readers and streams with another encoding) is counted in the bytes of its UTF-8 form.
The 1-based line of the input where the current record starts, counting every line: those inside quoted fields, empty lines, comments and the header too. Requires CsvReaderOptions.TrackLineNumbers.
The raw bytes of the current record as they appear in the input (UTF-8), without the line break. Empty before the first record and at the end of the input. Valid until the next read.
The separator in use. When it is detected from the input, getting it before the first Read reads the header (or the start of the input) synchronously. '\0' in CsvMode.FixedWidth mode, which has no separator.
The input. It must not change while the reader is in use. A UTF-16 or UTF-32 byte order mark, or CsvReaderOptions.Encoding, makes the reader transcode it instead.
Creates a reader over UTF-8 bytes in memory. The array is read in place without copying.
Parameters
utf8byte[]
The input. It must not change while the reader is in use. A UTF-16 or UTF-32 byte order mark, or CsvReaderOptions.Encoding, makes the reader transcode it instead.
The input, read from its current position. A UTF-16 or UTF-32 byte order mark sets its encoding; otherwise it is CsvReaderOptions.Encoding, or UTF-8 when that is not set.
options sets a character that is not ASCII, or uses the same character for two purposes; or, for a CsvSeparatorStream, sets another separator or FixedWidth mode, or a character its separator contains.
Returns the buffers to their pools and disposes the stream or text reader being read, unless the reader was created with leaveOpen. Calling it again does nothing.
Returns the buffers to their pools and asynchronously disposes the stream or text reader being read, unless the reader was created with leaveOpen. Calling it again does nothing.
publicIEnumerable<T> EnumerateRecords<T>(Trecord) whereT : class
Reads the remaining records into record, one instance of a CsvRecordAttribute class that is refilled for every record, so that reading allocates no object per record (strings are still created, unless CsvReaderOptions.PoolStrings shares them). Each step of the enumeration returns the same instance: copy what you keep. The header is read and the columns are resolved by this call.
Every member that a record sets is set again: a member whose column is optional and missing from the header keeps the value it has. Members whose type is itself a record type get a new instance for each record.
Asynchronously reads the remaining records into record, one instance that is refilled for every record. See EnumerateRecords; as in GetRecordsAsync, every exception is thrown by the enumeration.
The bytes of the field (UTF-8), including any quotes, escaped quotes, escape characters, padding and surrounding whitespace. Valid until the next read.
Reads the current record as a T, a CsvRecordAttribute type. The mapper is created on the first call (reading the header if needed) and reused while T stays the same.
The record cannot be read, such as a field that cannot be parsed as the type of its member. The exception names the record, the field and the member, and the cause is its Exception.InnerException.
Reads the current record with mapper, such as a projection that reads only some members (CsvRecordMapper.CreateProjection) or a hand-written mapper. A failure is reported as by GetRecord<T>().
The record cannot be read, such as a field that cannot be parsed as the type of its member. The exception names the record, the field and the member, and the cause is its Exception.InnerException.
The record cannot be read, such as a field that cannot be parsed as the type of its member. The exception names the record, the field and the member, and the cause is its Exception.InnerException.
Reads the remaining records as T, a CsvRecordAttribute type. The header is read and the columns are resolved by this call; the records are read as they are enumerated, and the exceptions of Read are thrown by the enumeration. A record that cannot be read as T throws a CsvFormatException that names the record, the field and the member, with the exception of the field or member as its Exception.InnerException, unless CsvReaderOptions.OnRecordError skips it.
Asynchronously reads the remaining records as T, a CsvRecordAttribute type. The header is read and the columns are resolved when the enumeration starts, so every exception, including those of GetRecords, is thrown by the enumeration.
Gets the value of field index of the current record as UTF-8 bytes, decoded as the CsvReaderOptions.Mode says: with quotes removed and escaped quotes unescaped, with escape characters removed, or as the text of its column. The span is valid until the next read.
The selector gets a reader positioned on one record. Use its field accessors (GetString(int), GetSpan(int), Parse<T>(int, Func<string, IFormatProvider, T>) and so on) and return a value; the reader and its spans are only valid during the call, and Read cannot be called on it. Calls run concurrently on different threads, each thread with its own reader. The input is read and checked by one thread at a time, in order, so CsvReaderOptions.OnMalformedRecord is never called concurrently. Exceptions from the reader or the selector are thrown by the enumerator after the results of the preceding records. This reader must not be used otherwise until the enumeration ends.
double and float exactly rounded, in NumberStyles.Float style with thousands separators allowed. A value beyond the range of the type is an infinity, except on .NET Framework when the .NET Framework parser parses it (with a format provider whose decimal separator is not ".", for example), which throws OverflowException.
Enums: the names of the members and their CsvEnumNameAttribute names, ignoring ASCII case unless two names of different members differ only in case, then numbers and lists of flags (Read, Write) as Enum.TryParse reads them, ignoring case. Enums are not ISpanParsable<T>, so on .NET 8 and later Parse<MyEnum> calls the overload of CsvEnumExtensions, an extension method.
double and float exactly rounded, in NumberStyles.Float style with thousands separators allowed. A value beyond the range of the type is an infinity, except on .NET Framework when the .NET Framework parser parses it (with a format provider whose decimal separator is not ".", for example), which throws OverflowException.
Enums: the names of the members and their CsvEnumNameAttribute names, ignoring ASCII case unless two names of different members differ only in case, then numbers and lists of flags (Read, Write) as Enum.TryParse reads them, ignoring case. Enums are not ISpanParsable<T>, so on .NET 8 and later Parse<MyEnum> calls the overload of CsvEnumExtensions, an extension method.
Parses field index as a DateTime or DateTimeOffset in exactly one of the given formats, tried in order, such as "yyyy-MM-dd" and "dd/MM/yyyy" for a file that mixes them, with the configured format provider. See ParseExact<T>(int, string).
Advances to the next record, reading the header first if it has not been read. Empty lines, comments and the records that the options say to skip are passed over.
Returns
bool
true when the reader is positioned on a record; false at the end of the input.
Asynchronously advances to the next record, reading the header first if it has not been read. Completes synchronously while records that have already been read from the source remain. See Read.
Reads the header row (if any) without advancing to the first data record. Without a header, it still reads the start of the input, so that Separator is known. Once the header has been read, calling it while the reader is on a record makes that record the new header, for files with several data sets one after the other: Headers, the by-name lookups and the record mappers created afterwards use it, and the record does not count as a data record. Otherwise it does nothing.
Finds the index of the column with the given header name. An exact match wins; otherwise the name is matched ignoring case and surrounding whitespace. Both names are changed by CsvReaderOptions.NormalizeHeaderName first, if it is set. Reads the header first if it has not been read (see ReadHeader).
Parameters
namestring
The header name of the column.
indexint
The 0-based index of the column, or 0 when it is not found.
Finds the index of a column whose name several columns have: the one at nameIndex among them, in header order. Names are matched as by TryGetFieldIndex(string, out int): among the exact matches when there are enough, else among those that match ignoring case and surrounding whitespace.
Parameters
namestring
The header name of the column.
nameIndexint
0 for the first column with that name, 1 for the second, and so on.
indexint
The 0-based index of the column, or 0 when it is not found.
Returns
bool
Whether the header has that many columns with the name.
The missing columns, as the message of ValidateHeader names them: the member and the header name it looks for, such as Order.Price ('Unit Price'). Empty when none is missing.
Checks, without reading a record, that the header has every column that T requires: the check that GetRecords makes. The header is read if it has not been.