C# 讀取 .dbf 檔案

最近需要讀取到上古資料庫檔案 dbf ,連資料檢視、瀏覽工具都快找不到了,最後是用了這款 VS Code 的擴充套件:DBF viewer,不過這個套件有個小問題,就是不能選擇編碼格式,所以瀏覽出來會是亂碼。
不過其實也不需要使用工具瀏覽,畢竟我們可以使用程式讀取 dbf 資料

安裝

筆者是使用網友開發的擴充套件, 先使用 NuGet 安裝 DbfDataReader 套件,或是使用 .NET CLI 執行以下指令安裝
	
dotnet add package DbfDataReader
    

使用示範

    
string dbfPath = @"my.dbf";
using var dbfTable = new DbfTable(dbfPath);

// 欄位資訊
foreach (var dbfColumn in dbfTable.Columns)
{
    var name = dbfColumn.ColumnName;
    var columnType = dbfColumn.ColumnType;
    var length = dbfColumn.Length;
    var decimalCount = dbfColumn.DecimalCount;

    Console.WriteLine($"欄位名稱: {name}, 資料型態: {columnType}, 長度: {length}, 小數位數: {decimalCount}");
}

// 逐筆資料
var skipDeleted = true;
var dbfRecord = new DbfRecord(dbfTable);
while (dbfTable.Read(dbfRecord))
{
    if (skipDeleted && dbfRecord.IsDeleted) continue;

    string data = "";
    foreach (var dbfValue in dbfRecord.Values)
    {
        var stringValue = dbfValue.ToString();

        data += $"\"{stringValue}\",";
    }

    Console.WriteLine(data.TrimEnd(','));
}
    

在 DbfTable 中可以透過附加參數來指定編碼格式:
    
using (var dbfTable = new DbfTable(dbfPath, Encoding.UTF8))
    

如果需要使用 Big5 (950) 可以查看這篇: C# .NET Core 以 Big 5 (大五碼)編碼格式讀取檔案

將 dbf 欄位對應到物件屬性

筆者遇到的情況是 dbf 資料都是字串格式,未針對其他資料格式調整,有使用的網友需要注意一下

筆者建立了 DbfFieldAttribute 這個屬性,用來標記類別中屬性對應到的 dbf 欄位名稱
    
[AttributeUsage(AttributeTargets.Property)]
public class DbfFieldAttribute : Attribute
{
    public string FieldName { get; }

    public DbfFieldAttribute(string fieldName)
    {
        FieldName = fieldName;
    }
}
    

建立 DbfExtensions 擴充方法,如果 dbf 欄位對應類別有使用 DbfFieldAttribute 標記資料表欄位名稱,就優先讀取,不然就使用物件屬性名稱直接找資料表欄位名稱:
    
public static class DbfExtensions
{
    public static List<T> ReadDbfEntities<T>(string dbfPath, Encoding encoding, bool skipDeleted = true) where T : new()
    {
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

        using var dbfTable = new DbfTable(dbfPath, encoding);

        var entities = new List<T>();
        var dbfRecord = new DbfRecord(dbfTable);

        while (dbfTable.Read(dbfRecord))
        {
            if (skipDeleted && dbfRecord.IsDeleted) continue;

            T entity = new T();
            for (int i = 0; i < dbfRecord.Values.Count; i++)
            {
                var dbfValue = dbfRecord.Values[i];
                var stringValue = dbfValue.ToString();
                var columnName = dbfTable.Columns[i].ColumnName;

                var property =
                    typeof(T).GetProperties()
                        .FirstOrDefault(p => p.GetCustomAttribute<DbfFieldAttribute>()?.FieldName == columnName) ??
                    typeof(T).GetProperty(columnName, BindingFlags.Public | BindingFlags.Instance);

                if (property != null && property.CanWrite)
                    property.SetValue(entity, stringValue);
            }

            entities.Add(entity);
        }

        return entities;
    }
}
    

建立 dbf 資料表欄位對應物件,例如資料表欄位(Column)有 6 的,分別是 T1 ~ T6 ,如果 Column 名稱和屬性名稱不一樣的話就可以使用剛剛建立的 DbfFieldAttribute 標記,如果一樣(如下)其實可以省略
    
public class DbfEntity
{
    [DbfField("T1")] public string T1 { get; set; } = string.Empty;
    [DbfField("T2")] public string T2 { get; set; } = string.Empty;
    [DbfField("T3")] public string T3 { get; set; } = string.Empty;
    [DbfField("T4")] public string T4 { get; set; } = string.Empty;
    [DbfField("T5")] public string T5 { get; set; } = string.Empty;
    [DbfField("T6")] public string T6 { get; set; } = string.Empty;
}
    

這樣要使用時就非常簡單了,很簡短的就讀取 dbf 檔案並轉換為物件清單:
    
string dbfPath = @"my.dbf";
var entities = DbfExtensions.ReadDbfEntities<DbfEntity>(dbfPath, Encoding.UTF8);
    



參考資料:
GitHub - yellowfeather/DbfDataReader

留言