有个需求,在c#前端读取dbf中的数据。网上搜索到的大部分都是配置ODBC方式的连接去读取的,还得安装驱动。因为最终客户端不能要求每个客户都去安装foxpro驱动,故此此处实现直接用代码去读取dbf数据。
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
namespace SBLWindowsClient.Common
{
/// <summary>
///
/// Function: 读取DBF文件数据到List
/// Note: 20200901-JML
/// Demo:
/// var reader = new DbfReader<TestModel>();
/// if (reader.Read(DBFFile))
/// List<TestModel> dataList = reader.GetDataList() as List<TestModel>;
/// Tip: 泛型<T>中的属性要小写来适配
/// (为兼容不同DBF文件中字段大小写不一致的问题
/// 此工具类读取表头时统一转小写
/// 所以要求最后转出的泛型类T中属性小写来适配)
/// </summary>
public class DbfReader<T>
{
private string FileName = string.Empty;
private bool FileIsOpened = false;
private System.IO.FileStream FileStream = null;
private System.IO.BinaryReader BinaryReader = null;
private DBFHeader DbfHeader = null;
private DBFField[] DbfFields = null;
private uint RecordCount = 0;
private uint FieldCount = 0;
private byte[] RecordBuffer;
private int RecordIndex = -1;
public DbfReader()
{
//注册EncodingProvider
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
}
public bool Read(string fileName)
{
try
{
this.FileName = this.ResolutionPath(fileName);
//打开DBF文件
if (!this.OpenFile())
{
this.Close();
return false;
}
//读取文件头部信息
if (!this.ReadFileHeader())
{
this.Close();
return false;
}
//读取表头字段
if (!this.ReadFields())
{
this.Close();
return false;
}
// 分配记录缓冲区
if (!this.AllocateRecordBuffer())
{
this.Close();
return false;
}
//成功读取DBF文件
return true;
}
catch(Exception e)
{
//TODO: Handle Exception
}
return false;
}
public List<T> GetDataList()
{
List<T> DataList = new List<T>();
try
{
//读取表头
PropertyInfo[] fieldList = new PropertyInfo[this.FieldCount];
Type type = typeof(T);
for (uint i = 0; i < this.FieldCount; i++)
{
//此处将DBF中的字段名称统一转小写
//对应的<T>Model属性也应该全小写来适配
string fieldName = this.GetFieldName(i).ToLower();
PropertyInfo propertyInfo = type.GetProperty(fieldName);
fieldList[i] = propertyInfo;
//泛型类T和DBF不匹配直接返回null
if(propertyInfo == null)
{
return null;
}
}
//读取数据
this.MoveFirst();
while (!this.IsEndRecord)
{
//创建对象
T item = System.Activator.CreateInstance<T>();
//属性赋值
for (uint i = 0; i < this.FieldCount; i++)
{
string fieldValue = this.GetFieldValue(i);
PropertyInfo propertyInfo = fieldList[i];
propertyInfo.SetValue(item, fieldValue);
}
//添加到list
DataList.Add(item);
//定位到下一行
this.MoveNext();
}
//关闭流
this.Close();
}
catch (Exception e)
{
//TODO: Handle Exception
}
return DataList;
}
public List<T> GetDataListByFilter(Func<T, bool> filter)
{
List<T> DataList = new List<T>();
try
{
//读取表头
PropertyInfo[] fieldList = new PropertyInfo[this.FieldCount];
Type type = typeof(T);
for (uint i = 0; i < this.FieldCount; i++)
{
//此处将DBF中的字段名称统一转小写
//对应的<T>Model属性也应该全小写来适配
string fieldName = this.GetFieldName(i).ToLower();
PropertyInfo propertyInfo = type.GetProperty(fieldName);
fieldList[i] = propertyInfo;
//泛型类T和DBF不匹配直接返回null
if (propertyInfo == null)
{
return null;
}
}
//读取数据
this.MoveFirst();
while (!this.IsEndRecord)
{
//创建对象
T item = System.Activator.CreateInstance<T>();
//属性赋值
for (uint i = 0; i < this.FieldCount; i++)
{
string fieldValue = this.GetFieldValue(i);
PropertyInfo propertyInfo = fieldList[i];
propertyInfo.SetValue(item, fieldValue);
}
//添加到list
if (filter(item))
{
DataList.Add(item);
}
//定位到下一行
this.MoveNext();
}
//关闭流
this.Close();
}
catch (Exception e)
{
//TODO: Handle Exception
}
return DataList;
}
private bool IsBeginRecord
{
get
{
return (-1 == this.RecordIndex);
}
}
private bool IsEndRecord
{
get
{
return ((uint)this.RecordIndex == this.RecordCount);
}
}
private bool OpenFile()
{
try
{
//如果文件已被打开,关闭重新打开
if (this.FileIsOpened)
{
this.Close();
}
//文件不为空
if (this.FileName == null || this.FileName == string.Empty || this.FileName.Length == 0)
{
return false;
}
//打开DBF文件
this.FileStream = File.Open(this.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
this.BinaryReader = new BinaryReader(this.FileStream, System.Text.Encoding.Default);
this.FileIsOpened = true;
return true;
}
catch(Exception e)
{
//TODO: Handle Exception
}
return false;
}
private bool ReadFileHeader()
{
try
{
if(this.DbfHeader == null)
{
this.DbfHeader = new DBFHeader();
}
this.DbfHeader.Version = this.BinaryReader.ReadSByte();
this.DbfHeader.LastModifyYear = this.BinaryReader.ReadByte();
this.DbfHeader.LastModifyMonth = this.BinaryReader.ReadByte();
this.DbfHeader.LastModifyDay = this.BinaryReader.ReadByte();
this.DbfHeader.RecordCount = this.BinaryReader.ReadUInt32();
this.DbfHeader.HeaderLength = this.BinaryReader.ReadUInt16();
this.DbfHeader.RecordLength = this.BinaryReader.ReadUInt16();
this.DbfHeader.Reserved = this.BinaryReader.ReadBytes(16);
this.DbfHeader.TableFlag = this.BinaryReader.ReadSByte();
this.DbfHeader.CodePageFlag = this.BinaryReader.ReadSByte();
this.DbfHeader.Reserved2 = this.BinaryReader.ReadBytes(2);
//计算总条数和字段数目
this.RecordCount = this.DbfHeader.RecordCount;
this.FieldCount = 0;
uint fieldCount = (uint)((this.DbfHeader.HeaderLength - DBFHeader.DBFHeaderSize - 1) / DBFField.DBFFieldSize);
for (uint i = 0; i < fieldCount; i++)
{
this.FileStream.Seek(DBFHeader.DBFHeaderSize + i * DBFField.DBFFieldSize, SeekOrigin.Begin);
byte flag = this.BinaryReader.ReadByte();
if (0x0D != flag)
{
this.FieldCount++;
}
else
{
break;
}
}
return true;
}
catch (Exception e)
{
//TODO: Handle Exception
}
return false;
}
private bool ReadFields()
{
try
{
if (this.DbfFields == null)
{
this.DbfFields = new DBFField[this.FieldCount];
}
this.FileStream.Seek(DBFHeader.DBFHeaderSize, SeekOrigin.Begin);
for (int i = 0; i < this.FieldCount; i++)
{
this.DbfFields[i] = new DBFField();
this.DbfFields[i].Name = this.BinaryReader.ReadBytes(11);
this.DbfFields[i].Type = this.BinaryReader.ReadSByte();
this.DbfFields[i].Offset = this.BinaryReader.ReadUInt32();
this.DbfFields[i].Length = this.BinaryReader.ReadByte();
this.DbfFields[i].Precision = this.BinaryReader.ReadByte();
this.DbfFields[i].Reserved = this.BinaryReader.ReadBytes(2);
this.DbfFields[i].DbaseivID = this.BinaryReader.ReadSByte();
this.DbfFields[i].Reserved2 = this.BinaryReader.ReadBytes(10);
this.DbfFields[i].ProductionIndex = this.BinaryReader.ReadSByte();
}
return true;
}
catch (Exception e)
{
//TODO: Handle Exception
}
return false;
}
private bool AllocateRecordBuffer()
{
try
{
if (this.RecordBuffer == null)
{
this.RecordBuffer = new byte[this.DbfHeader.RecordLength];
}
if(this.RecordBuffer != null)
{
return true;
}
}
catch (Exception e)
{
//TODO: Handle Exception
}
return false;
}
private void Close()
{
try
{
try
{
this.FileStream.Close();
this.BinaryReader.Close();
}
catch (Exception e)
{
//TODO: Handle Exception
}
this.RecordBuffer = null;
this.DbfHeader = null;
this.DbfFields = null;
this.FileStream = null;
this.BinaryReader = null;
this.FileIsOpened = false;
this.FieldCount = 0;
this.RecordCount = 0;
this.RecordIndex = -1;
this.FileName = string.Empty;
}
catch (Exception e)
{
//TODO: Handle Exception
}
}
private string GetFieldName(uint fieldIndex)
{
try
{
string fieldName = System.Text.Encoding.Default.GetString(this.DbfFields[fieldIndex].Name);
int i = fieldName.IndexOf('\0');
if (i > 0)
{
fieldName = fieldName.Substring(0, i);
}
return fieldName;
}
catch (Exception e)
{
//TODO: Handle Exception
}
return string.Empty;
}
private string GetFieldValue(uint fieldIndex)
{
try
{
//从Buffer中读取
uint offset = 0;
if (offset == 0)
{
for (int i = 0; i < fieldIndex; i++)
{
offset += this.DbfFields[i].Length;
}
}
byte[] subBytes = GetSubBytes(this.RecordBuffer, offset, this.DbfFields[fieldIndex].Length);
//转换成值
string fieldValue = string.Empty;
if (((sbyte)'I') == this.DbfFields[fieldIndex].Type)
{
int num = Byte2Int32(subBytes);
fieldValue = num.ToString();
}
else if (((sbyte)'B') == this.DbfFields[fieldIndex].Type)
{
double num = Byte2Double(subBytes);
fieldValue = num.ToString();
}
else if (((sbyte)'Y') == this.DbfFields[fieldIndex].Type)
{
long num = Byte2Int64(subBytes);
fieldValue = (((decimal)num) / 10000).ToString();
}
else if (((sbyte)'D') == this.DbfFields[fieldIndex].Type)
{
DateTime date = Byte2Date(subBytes);
fieldValue = date.ToString();
}
else if (((sbyte)'T') == this.DbfFields[fieldIndex].Type)
{
DateTime date = Byte2DateTime(subBytes);
fieldValue = date.ToString();
}
else
{
//使用默认编码导致读取的中文字符乱码
//fieldValue = System.Text.Encoding.Default.GetString(subBytes);
//改为使用GB2312编码进行读取
//c#使用内嵌编码之外的编码要先进行注册, 此处已将注册代码移至构造方法中
//System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
fieldValue = System.Text.Encoding.GetEncoding("GB2312").GetString(subBytes);
}
//去除首尾空格
fieldValue = fieldValue.Trim();
//特殊处理: N - 数值型 F - 浮点型 L - 逻辑型 D - 日期型 T - 日期时间型
if (((sbyte)'N') == this.DbfFields[fieldIndex].Type ||
((sbyte)'F') == this.DbfFields[fieldIndex].Type)
{
if(fieldValue.Length == 0)
{
fieldValue = "0";
}
else if(fieldValue == ".")
{
fieldValue = "0";
}
else
{
try
{
fieldValue = System.Convert.ToDecimal(fieldValue).ToString();
}
catch (Exception)
{
fieldValue = "0";
}
}
}
else if (((sbyte)'L') == this.DbfFields[fieldIndex].Type)
{
if(fieldValue != "T")
{
fieldValue = "false";
}
else
{
fieldValue = "true";
}
}
else if (((sbyte)'D') == this.DbfFields[fieldIndex].Type || ((sbyte)'T') == this.DbfFields[fieldIndex].Type)
{
//Handle
}
//返回值
return fieldValue;
}
catch (Exception e)
{
//TODO: Handle Exception
}
return string.Empty;
}
private void MoveFirst()
{
try
{
if (this.IsBeginRecord && this.IsEndRecord)
{
return;
}
//指向第一条记录
this.RecordIndex = 0;
ReadCurrentRecord();
}
catch(Exception e)
{
//TODO: Handle Exception
}
return;
}
private void MovePrevious()
{
try
{
if (this.IsBeginRecord)
{
return;
}
//指向上一条记录
this.RecordIndex -= 1;
ReadCurrentRecord();
}
catch (Exception e)
{
//TODO: Handle Exception
}
return;
}
private void MoveNext()
{
try
{
if (this.IsEndRecord)
{
return;
}
//指向下一条记录
this.RecordIndex += 1;
ReadCurrentRecord();
}
catch (Exception e)
{
//TODO: Handle Exception
}
return;
}
private void MoveLast()
{
try
{
if (this.IsBeginRecord && this.IsEndRecord)
{
return;
}
//指向最后一条记录
this.RecordIndex = (int)this.RecordCount - 1;
ReadCurrentRecord();
}
catch (Exception e)
{
//TODO: Handle Exception
}
return;
}
private void ReadCurrentRecord()
{
try
{
if (this.IsBeginRecord && this.IsEndRecord)
{
return;
}
this.FileStream.Seek(this.DbfHeader.HeaderLength + this.DbfHeader.RecordLength * this.RecordIndex + 1, SeekOrigin.Begin);
this.RecordBuffer = this.BinaryReader.ReadBytes(this.DbfHeader.RecordLength);
}
catch(Exception e)
{
//TODO: Handle Exception
}
}
private byte[] GetSubBytes(byte[] buf, uint startIndex, long length)
{
if (buf == null)
{
throw new ArgumentNullException("buf");
}
if (startIndex >= buf.Length)
{
throw new ArgumentOutOfRangeException("startIndex");
}
if (0 == length)
{
throw new ArgumentOutOfRangeException("length", "参数length必须大于0");
}
if (length > buf.Length - startIndex)
{
length = buf.Length - startIndex;
}
//逐位复制
byte[] target = new byte[length];
for (uint i = 0; i < length; i++)
{
target[i] = buf[startIndex + i];
}
return target;
}
private int Byte2Int32(byte[] buf)
{
if (buf == null)
{
throw new ArgumentNullException("buf");
}
if (4 != buf.Length)
{
throw new ArgumentException("函数Byte2Int32(byte[])的参数必须是长度为4的有效byte数组", "buf");
}
// byte[] 解码成 int
return (int)((((buf[0] & 0xff) | (buf[1] << 8)) | (buf[2] << 0x10)) | (buf[3] << 0x18));
}
private long Byte2Int64(byte[] buf)
{
if (null == buf)
{
throw new ArgumentNullException("buf");
}
if (8 != buf.Length)
{
throw new ArgumentException("函数Byte2Int64(byte[])的参数必须是长度为8的有效byte数组", "buf");
}
// byte[] 解码成 long
uint num1 = (uint)(((buf[0] | (buf[1] << 8)) | (buf[2] << 0x10)) | (buf[3] << 0x18));
uint num2 = (uint)(((buf[4] | (buf[5] << 8)) | (buf[6] << 0x10)) | (buf[7] << 0x18));
return (long)(((ulong)num2 << 0x20) | num1);
}
private double Byte2Double(byte[] buf)
{
if (null == buf)
{
throw new ArgumentNullException("buf");
}
if (8 != buf.Length)
{
throw new ArgumentException("函数Byte2Double(byte[])的参数必须是长度为8的有效byte数组", "buf");
}
double num1 = 0;
unsafe
{
fixed (byte* numRef1 = buf)
{
num1 = *((double*)numRef1);
}
}
return num1;
}
private DateTime Byte2Date(byte[] buf)
{
if (null == buf)
{
throw new ArgumentNullException("buf");
}
if (8 != buf.Length)
{
throw new ArgumentException("函数Byte2DateTime(byte[])的参数必须是长度为8的有效byte数组", "buf");
}
try
{
string str1 = System.Text.Encoding.Default.GetString(buf);
str1 = str1.Trim();
if (str1.Length < 8)
{
return new DateTime();
}
int year = int.Parse(str1.Substring(0, 4));
int month = int.Parse(str1.Substring(4, 2));
int day = int.Parse(str1.Substring(6, 2));
return new DateTime(year, month, day);
}
catch (Exception e)
{
throw e;
}
}
private DateTime Byte2DateTime(byte[] buf)
{
if (null == buf)
{
throw new ArgumentNullException("buf");
}
if (8 != buf.Length)
{
throw new ArgumentException("函数Byte2DateTime(byte[])的参数必须是长度为8的有效byte数组", "buf");
}
try
{
byte[] tmp = GetSubBytes(buf, 0, 4);
tmp.Initialize();
// 获取天数
int days = Byte2Int32(tmp);
// 获取毫秒数
tmp = GetSubBytes(buf, 4, 4);
int milliSeconds = Byte2Int32(tmp);
// 在最小日期时间的基础上添加刚获取的天数和毫秒数,得到日期字段数值
DateTime dm1 = DateTime.MinValue;
dm1 = dm1.AddDays(days - 1721426);
dm1 = dm1.AddMilliseconds((double)milliSeconds);
return dm1;
}
catch
{
return new DateTime();
}
}
private string ResolutionPath(string str)
{
try
{
string result = str;
DateTime time = DateTime.Now;
Match m = Regex.Match(str, @"\$\{(.*?)\}");
while (m.Success)
{
var val = m.Groups[0].Value;
var f = m.Groups[1].Value;
var t = time.ToString(f);
result = result.Replace(val, t);
m = m.NextMatch();
}
return result;
}
catch (Exception)
{
//TODO: Handel Exception
}
return str;
}
}
internal class DBFHeader
{
public const int DBFHeaderSize = 32;
public sbyte Version;
public byte LastModifyYear;
public byte LastModifyMonth;
public byte LastModifyDay;
public uint RecordCount;
public ushort HeaderLength;
public ushort RecordLength;
public byte[] Reserved = new byte[16];
public sbyte TableFlag;
public sbyte CodePageFlag;
public byte[] Reserved2 = new byte[2];
}
internal class DBFField
{
public const int DBFFieldSize = 32;
public byte[] Name = new byte[11];
public sbyte Type;
public uint Offset;
public byte Length;
public byte Precision;
public byte[] Reserved = new byte[2];
public sbyte DbaseivID;
public byte[] Reserved2 = new byte[10];
public sbyte ProductionIndex;
}
}