最近需要讀取到上古資料庫檔案 dbf ,連資料檢視、瀏覽工具都快找不到了,最後是用了這款 VS Code 的擴充套件:DBF viewer,不過這個套件有個小問題,就是不能選擇編碼格式,所以瀏覽出來會是亂碼。
不過其實也不需要使用工具瀏覽,畢竟我們可以使用程式讀取 dbf 資料
在 DbfTable 中可以透過附加參數來指定編碼格式:
如果需要使用 Big5 (950) 可以查看這篇: C# .NET Core 以 Big 5 (大五碼)編碼格式讀取檔案
筆者建立了 DbfFieldAttribute 這個屬性,用來標記類別中屬性對應到的 dbf 欄位名稱
建立 DbfExtensions 擴充方法,如果 dbf 欄位對應類別有使用 DbfFieldAttribute 標記資料表欄位名稱,就優先讀取,不然就使用物件屬性名稱直接找資料表欄位名稱:
建立 dbf 資料表欄位對應物件,例如資料表欄位(Column)有 6 的,分別是 T1 ~ T6 ,如果 Column 名稱和屬性名稱不一樣的話就可以使用剛剛建立的 DbfFieldAttribute 標記,如果一樣(如下)其實可以省略
這樣要使用時就非常簡單了,很簡短的就讀取 dbf 檔案並轉換為物件清單:
參考資料:
GitHub - yellowfeather/DbfDataReader
不過其實也不需要使用工具瀏覽,畢竟我們可以使用程式讀取 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
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com