ExcelReader.NET
4.1.0
dotnet add package ExcelReader.NET --version 4.1.0
NuGet\Install-Package ExcelReader.NET -Version 4.1.0
<PackageReference Include="ExcelReader.NET" Version="4.1.0" />
<PackageVersion Include="ExcelReader.NET" Version="4.1.0" />
<PackageReference Include="ExcelReader.NET" />
paket add ExcelReader.NET --version 4.1.0
#r "nuget: ExcelReader.NET, 4.1.0"
#:package ExcelReader.NET@4.1.0
#addin nuget:?package=ExcelReader.NET&version=4.1.0
#tool nuget:?package=ExcelReader.NET&version=4.1.0
ExcelReader
High-performance Excel reading and writing for .NET 10. Reads .xlsx, .xlsb, .xls, and .csv; writes .xlsx, .xlsb, .xls, and .csv.
ExcelReader is built for streaming spreadsheet workloads where low allocations matter. It reads worksheet rows as lightweight ref struct values, resolves shared strings, recognizes date styles, and handles sparse cells. Workbooks open from a path, a Stream, or a caller-owned ReadOnlyMemory<byte> — the in-memory path decompresses parts directly, without a ZipArchive or an intermediate stream.
Install
dotnet add package ExcelReader.NET
using ExcelReader.Core.Reader;
using IExcelRowReader reader = Excel.Open("book.xlsx");
foreach (Row row in reader)
{
foreach (RowCell cell in row.Cells)
{
Console.Write(cell.Value.GetString());
Console.Write('\t');
}
Console.WriteLine();
}
Guide
- Reading rows — opening a workbook, enumerating rows, auto-detecting the format, async reads, prefetch decompression, and the ADO.NET
IDataReaderbridge. - Typed parsing — binding rows to your own types with attributes, the compile-time generator (Native AOT / trimming), the fluent API, required columns, custom converters, and the zero-copy
ref structpath. - Writing workbooks — XLSX, XLSB (BIFF12) and XLS (BIFF8) writers, cell styles, typed records, and prefetch compression.
- CSV — reading, dialect sniffing, parallel parsing, and writing delimited text.
- Encrypted workbooks — opening password-protected packages and encrypting written ones.
- Benchmarks — throughput and allocation against CsvHelper, Sep, Sylvan, MiniExcel and SpreadCheetah. Live results.
Command line
dotnet tool install --global ExcelReader.NET.Cli
excelreader sheets book.xlsb # 0<TAB>Sheet1
excelreader schema book.xlsb --sample-size 500 # 0<TAB>Id<TAB>Int64
excelreader convert book.xlsb --output data.csv # convert to another format, by extension
excelreader convert book.xlsb --output data.xlsx # .xlsx, .xlsb, .xls and .csv all work
excelreader convert book.xlsb --format xlsx | head -c 4 # to stdout, --format picks it instead
Flags: --sheet|-s <name|index>, --header-row N (0 = no header), --sample-size N,
--output|-o <file>, --format|-f <xlsx|xlsb|xls|csv> (defaults to --output's extension, or csv
for stdout), --delimiter|-d <char> (csv only). Run excelreader <command> --help for the full list.
--sheet matches a sheet name first and falls back to a 0-based index, so a sheet literally named
2 is reachable by name. sheets and schema write plain tab-separated text. convert reports progress on stderr while it
runs, unless stderr is redirected. Exit codes are 0 ok and 1 failure; results go to stdout and
errors to stderr, so convert is safe to pipe.
Notes
- Reads
.xlsx,.xlsb(BIFF12),.xls(BIFF8), and.csv; writes.xlsx,.xlsb,.xls, and.csv. - Reads one sheet at a time (XLSX/XLSB/XLS); use
MoveToSheet(index)orTryMoveToSheet(name)to switch sheets. CSV has no sheets. - Missing cells in sparse rows are exposed as empty cells.
- String conversion allocates only when you call
GetString(). - The XLSX scanner accepts the SpreadsheetML shapes commonly emitted by non-Excel producers, including single-quoted attributes, comments in
sheetData, and CDATA text runs. - Readers bound untrusted input by default: 512 MB total decompressed ZIP data, 32 MB per cell/row value buffer, and 128 MB for shared strings. Pass
ExcelReaderOptionsto theExcel.From*/Excel.Open*factories to tune these limits; set a limit to0to opt out and restore unlimited behavior for that limit.CsvReaderhas its ownCsvReaderOptions.MaxCellBytes(default 32 MB) for the same purpose. - The XLSX writer emits a compact workbook with strings, numbers, booleans, dates, and blank cells; shared strings are opt-in.
- The XLSB writer emits BIFF12 workbook parts inside the standard XLSB ZIP package; shared strings are opt-in.
- The XLS writer buffers records in memory and assembles the OLE container at
EndAsync; choose it when write throughput matters more than peak allocation. Excel.EncryptPackage/EncryptPackageAsyncwrap a written XLSX/XLSB package in agile ECMA-376 encryption; see Encrypted workbooks.
Build
dotnet restore ExcelReader.slnx
dotnet build ExcelReader.slnx --configuration Release
dotnet test --project tests/ExcelReader.Tests/ExcelReader.Tests.csproj --configuration Release
Other languages
ExcelReader ships a NativeAOT shared library with a C ABI, so non-.NET languages can read and write XLSX, XLSB, XLS and CSV without a .NET runtime installed.
- C ABI header:
src/ExcelReader.Native/include/excelreader.h - Python package:
python/ - C++ package: header-only CMake wrapper,
xl::Workbook/xl::parse_sheet/xl::write_sheetover the same ABI — seecpp/README.md. - Rust crate: safe
Workbook/parse_sheet/write_sheetbindings, downloadable viacargo add excelreader— seerust/excelreader/README.md.
from excelreader import open_workbook
with open_workbook("book.xlsx") as workbook:
for row in workbook.rows():
print([cell.value for cell in row])
Writing goes through one export, xl_write_typed: a whole sheet in a single call, from columnar
buffers the ABI borrows rather than copies. All three bindings expose it — Python as
write_workbook/write_arrow/write_pandas/write_polars, C++ as xl::write_columns and
xl::write_sheet<T>, Rust as writer::write_columns and writer::write_sheet. In C++ and Rust the
same struct mapping drives both directions, so reading a sheet and writing it back needs one
mapping, not two:
use excelreader::writer::write_sheet;
use excelreader::XL_FORMAT_XLSX;
write_sheet("out.xlsx", XL_FORMAT_XLSX, &rows, None)?;
auto written = xl::write_sheet("out.xlsx", rows); // format inferred from the extension
Row-by-row decoded reads are available from all three bindings — Python as Workbook.rows(), C++
as xl::Workbook::rows(), Rust as Workbook::rows(). Python additionally exposes
read_all_columnar() over xl_read_all_blob, which the other two do not wrap.
The Arrow export is available from Python
(to_arrow/to_record_batch), C++ (xl::parse_arrow<T>, in the separate <xl/excelreader_arrow.hpp>
header — no Apache Arrow C++ dependency, you get the raw C Data Interface pair), and Rust
(excelreader::arrow::parse_arrow, behind the arrow cargo feature, returning an arrow::array::RecordBatch).
All three bindings can also read a sheet a batch at a time instead of the whole sheet in one call, so
peak memory is one batch rather than one sheet — Python as iter_parse_typed/
to_record_batch_reader (plus iter_pandas/iter_polars), C++ as xl::typed_reader<T>/
xl::arrow_stream<T>, Rust as Workbook::typed_chunks/arrow::parse_arrow_stream. The .NET reader
above already streams row-by-row by construction, so there is nothing to add here for it; see each
binding's README for the chunked-reading semantics (one chunked read per workbook, batch_size
meaning, and what invalidates a live one).
Encrypting a written package goes through one export too, xl_encrypt_package — wrap a finished
plaintext XLSX/XLSB package in agile ECMA-376 encryption, given a password. All three bindings
expose it: Python as encrypt_package, C++ as xl::encrypt_package, Rust as
writer::encrypt_package; see each binding's README for the encrypted-workbooks section.
Contributing
See ARCHITECTURE.md for a map of the codebase, STYLEGUIDE.md for the code style, and CONTRIBUTING.md for build expectations and how to submit a change. Security issues should go through the private channel in SECURITY.md, not a public issue.
License
ExcelReader is licensed under the MIT License. See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on ExcelReader.NET:
| Package | Downloads |
|---|---|
|
ExcelReader.Arrow
Apache Arrow RecordBatch conversion for ExcelReader.NET. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.1.0 | 49 | 9/21/2026 |
| 4.0.0 | 56 | 9/20/2026 |
| 3.1.0 | 117 | 9/14/2026 |
| 3.0.2 | 118 | 9/9/2026 |
| 3.0.1 | 3,853 | 9/4/2026 |
| 3.0.0 | 116 | 9/4/2026 |
| 2.3.0 | 1,802 | 8/30/2026 |
| 2.2.0 | 109 | 8/26/2026 |
| 2.1.3 | 142 | 8/20/2026 |
| 2.1.2 | 2,904 | 8/16/2026 |
| 2.1.1 | 100 | 8/14/2026 |
| 2.1.0 | 104 | 8/14/2026 |
| 2.0.1 | 108 | 8/5/2026 |
| 2.0.0 | 172 | 8/5/2026 |
| 1.4.5 | 118 | 8/4/2026 |
| 1.4.4 | 121 | 7/31/2026 |
| 1.4.2 | 146 | 7/28/2026 |
| 1.4.1 | 134 | 7/23/2026 |
| 1.4.0 | 106 | 7/22/2026 |
| 1.3.1 | 115 | 7/21/2026 |
See the changelog: https://github.com/GabrielMarquezMatte/ExcelReader/releases