DbfTools 1.1.0
dotnet add package DbfTools --version 1.1.0
NuGet\Install-Package DbfTools -Version 1.1.0
<PackageReference Include="DbfTools" Version="1.1.0" />
<PackageVersion Include="DbfTools" Version="1.1.0" />
<PackageReference Include="DbfTools" />
paket add DbfTools --version 1.1.0
#r "nuget: DbfTools, 1.1.0"
#:package DbfTools@1.1.0
#addin nuget:?package=DbfTools&version=1.1.0
#tool nuget:?package=DbfTools&version=1.1.0
DbfTools
A .NET library that reads legacy dBASE/DBF files — parsing the binary format byte-by-byte via
struct marshalling — and maps records into strongly-typed objects or an ADO.NET DataTable.
Overview
DbfTools exists to make DBF files (the binary format behind dBASE, FoxPro, and many legacy
point-of-sale systems) consumable from ordinary .NET code without hand-rolling a binary parser.
It reads the header and field descriptors up front, validates the file structure before any
record is read, and maps each record either to a typed object (by reflection, matched on
property name) or to an ADO.NET DataTable.
Key Features
- Typed mapping —
DbfReader.ReadDbf<T>(path)maps DBF records to instances of your own class, matched to its public properties by name. DataTablemapping —DbfReader.ReadDataTable(path)maps every field to aDataColumnwith no external dependency, for callers that want ADO.NET-shaped data instead of a POCO.- Zero package dependencies — the library is a pure
netstandard2.0assembly over the BCL. Nothing transitive to resolve. - Fails loudly on corruption — a malformed DBF throws
DbfFormatExceptionrather than silently returning an empty or partial result. - Single assembly, two runtimes — one
netstandard2.0build serves both .NET Framework 4.8 and modern .NET. - Codepage-aware
'C'fields —'C'(character) fields decode through a codepage rather than plain ASCII; when the file's own header names a codepage this library ships, that codepage is used automatically, with no configuration required from the caller. See Character encoding.
Supported Target Frameworks
| Package TFM | Consumable from |
|---|---|
netstandard2.0 |
.NET Framework 4.8, .NET (Core) 3.1+, .NET 5–10 |
Installation
dotnet add package DbfTools
The command above is the full install step — DbfTools is published on nuget.org, so no private
feed, no credentials, and no nuget.config changes are required.
Usage
DbfTools exposes exactly two public reader paths, both on DbfTools.DbfReader. Both open the
file, validate the DBF header and field descriptors up front, and skip records flagged deleted
(0x2A). A structurally valid file with zero records returns an empty result from either
path — that is not an error, and neither path throws for it.
DbfReader.ReadDbf<T>(string dbfFile) — generic mapping
public static List<T> ReadDbf<T>(string dbfFile) where T : new()
Maps each non-deleted record to a new instance of T, matching DBF field names to T's public
instance properties by name, case-insensitive. A DBF column with no matching property is
silently skipped — its bytes are still consumed so the record cursor stays aligned for the
fields that follow.
public class Customer
{
public string Name { get; set; } = string.Empty;
public decimal Balance { get; set; }
public DateTime LastVisit { get; set; }
}
List<Customer> customers = DbfReader.ReadDbf<Customer>(@"C:\data\customers.dbf");
DbfReader.ReadDataTable(string dbfFile) — ADO.NET DataTable mapping
public static DataTable ReadDataTable(string dbfFile)
Maps every DBF field to a DataColumn — there is no mapped/unmapped distinction here, unlike
ReadDbf<T>(). Column CLR types come solely from each field's own declared type byte and
scale, never from sampled record data.
DataTable table = DbfReader.ReadDataTable(@"C:\data\customers.dbf");
String trimming differs between the two paths
ReadDbf<T>() trims padded 'C' (character) string fields before assigning them to a
property. ReadDataTable() does not trim — 'C' values come back space-padded to the
field's declared width. This is a pinned, by-design difference between the two readers, not a bug.
A consumer reading via ReadDataTable() who wants trimmed strings must call .Trim() themselves.
Error contract
Both reader paths share one failure contract:
- Malformed / structurally invalid input throws
DbfFormatException. It is asealedclass deriving directly fromSystem.Exception— notIOException— so a caller'scatch (IOException)will not accidentally swallow a format defect. It carries:DbfFormatReason Reason— which specific defect was detected (e.g.FileTooShortForHeader,MissingFieldDescriptorTerminator,TruncatedRecordData,UnsupportedFieldType,DuplicateFieldName).long Offset— the byte offset within the file at which the defect was found.
- A missing file throws
FileNotFoundException, unwrapped. The parser never read a byte, so wrapping it inDbfFormatExceptionwould misrepresent what failed. - A structurally valid file with zero records is not an error.
ReadDbf<T>()returns an emptyList<T>;ReadDataTable()returns aDataTablewith the correct columns and zero rows. Neither throws. Do not write defensive code around an empty result — it is the normal outcome for an empty table, not a failure signal.
A note on schema/statistics analysis (DbfFieldReader)
The library also contains a schema/min-max-statistics reader, DbfFieldReader, which computes
per-field min/max values for numeric columns. It is declared internal in the current source and
is not part of this package's public API surface — an external consumer cannot reference it at
all, and no usage example is shown here for that reason.
Character encoding
'C' (character) fields are decoded using a single-byte codepage. 'N', 'D', 'F' and 'L'
fields are unaffected — every legal byte in those types is ASCII by definition.
The codepage used for a 'C' field is resolved in this order:
- An
Encodingyou pass explicitly to the two-argument overload wins outright. - Otherwise, the codepage named by the file's own header language-driver byte (byte 29) — when that byte names one of the three codepages this library ships.
- Otherwise, cp1252. This covers both the unset value
0x00and any header byte naming a codepage this library does not ship.
You get all of this from the one-argument call — DbfReader.ReadDbf<T>(path) reads the header
byte and falls back on its own; no opt-in, no configuration.
The three built-in codepages are exposed as public Encoding statics — DbfEncodings.Cp1252,
DbfEncodings.Cp437, DbfEncodings.Cp850 — each usable on both .NET Framework 4.8 and modern .NET
with no provider registration and no package reference. They live in the root DbfTools namespace,
so a consumer already writing using DbfTools; to reach DbfReader needs no second using
directive.
Passing your own encoding
public static List<T> ReadDbf<T>(string dbfFile, Encoding encoding) where T : new()
public static DataTable ReadDataTable(string dbfFile, Encoding encoding)
Both overloads decode every 'C' field with the Encoding you pass instead of resolving one from
the file's header — rung 1 of the order above. Passing a null encoding throws
ArgumentNullException before any file is opened.
using DbfTools;
List<Customer> customers =
DbfReader.ReadDbf<Customer>(@"C:\data\customers.dbf", DbfEncodings.Cp437);
DataTable table = DbfReader.ReadDataTable(@"C:\data\customers.dbf", DbfEncodings.Cp437);
Codepages this library does not ship
A file whose header honestly names cp852, cp1250 or cp1251 (or any other codepage this library does not ship) falls through to the cp1252 fallback and decodes plausible-looking and wrong for every byte where the two codepages differ — no exception, no warning, no diagnostic.
The working fix: reference System.Text.Encoding.CodePages in the consuming application,
register the provider once, and pass the real codepage to the two-argument overload:
using System.Text;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
List<Item> items = DbfReader.ReadDbf<Item>(@"C:\data\items.dbf", Encoding.GetEncoding(852));
DbfTools never does this itself: registering a codepage provider mutates process-global encoding state on the host application's behalf, and the call succeeds on .NET Framework while throwing on modern .NET when no provider is registered — so the same file would decode differently depending on which runtime opened it, which is exactly the kind of silent, per-runtime divergence this release exists to remove.
Resolution is driven only by what the file declares about itself through its header
language-driver byte, never by guessing from record content — a wrong guess would reintroduce the
same silent corruption this behaviour exists to remove. Upgrading from 1.0.0 changes only values
that were already wrong: ASCII and cp1252 decode identically for every byte in 0x00–0x7F, so
pure-ASCII 'C' content is byte-for-byte unchanged, and only bytes above 0x7F — which previously
came back as ? — decode differently.
License
MIT — see LICENSE. Copyright (c) 2026 Jon Bailey.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.