C#之读取DBF文件到DataSet中
公用工具类
public class DBFPhaser { /* 字段信息结构 */ public struct Field { public byte[] name; /* 字段名变量 */ public byte type; /* 字段类型变量 */ public int addr; /* 字段数据的相对地址 */ public byte width, deC; /* 字段长度及小数位变量 */ } public class DBFDataRow { private byte[] _data; private Dictionary<string, Field> _dic; public DBFDataRow(byte[] RowData, Dictionary<string, Field> dic_fields) { _data = RowData; _dic = dic_fields; } /// <summary> /// 获取字段值 /// </summary> /// <param name="columnName">字段名称(不区分大小写)</param> /// <returns>字段值</returns> public string this[string columnName] { get { Field fd; if (_dic.TryGetValue(columnName.ToUpper(), out fd)) { return Encoding.GetEncoding(936).GetString(_data, fd.addr, fd.width).Trim("\0".ToCharArray()).Trim(); } else return ""; } } /// <summary> /// 当前行是否打了删除标记 /// </summary> public bool HasDeleted { get { return _data[0] == 42; } } } //文件头长度 public int HeadLen; //文件 private FileStream file; //文件偏移位,用于去除头字节 private int offset; //字段列表 public Dictionary<string, Field> fields = new Dictionary<string, Field>(); //行记录数 public int RowsCount; //字段数 public int FieldCount; //行长度 public int RowLen; //构造函数 public DBFPhaser(string filefullname, int offset = 0) { this.offset = offset; //只读模式打开文件 try { file = new FileStream(filefullname, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } catch (Exception ex) { throw new Exception("打开DBF文件[" + filefullname + "出错:" + ex.Message); } opendbf(); } public DBFPhaser(FileStream fs, int offset = 0) { this.offset = offset; file = fs; opendbf(); } //打开文件 public void opendbf() { Field filed; string s; byte[] buf = new byte[18]; int n; //文件指针复位 file.Seek(offset, SeekOrigin.Begin); n = file.Read(buf, 0, 4); //文件标志,年,月,日 if (n != 4) throw new Exception("DBF文件头格式不合法"); //行记录数 n = file.Read(buf, 0, 4); if (n != 4) throw new Exception("DBF文件头格式不合法,没有行记录数"); RowsCount = buf[0] + buf[1] * 256 + buf[2] * 65536 + buf[3] * 16777216; n = file.Read(buf, 0, 4); if (n != 4) throw new Exception("DBF文件头格式不合法,没有表头信息"); //表头长度 HeadLen = buf[0] + buf[1] * 256; //行长度 RowLen = buf[2] + buf[3] * 256; //字段个数,文件头长度=32+32*字段数目+1 //现场发现上海的prop文件导出来不会在文件头的最后加上结束标志0D,所以此处做个4舍5入处理 FieldCount = (int)Math.Round((HeadLen - 1 - 32) / 32.0); //获取字段信息 int y = 1; for (int i = 0; i < FieldCount; i++) { file.Seek(32 + 32 * i + offset, SeekOrigin.Begin);//文件指针指向记录处 file.Read(buf, 0, 18); filed = new Field(); filed.name = new byte[12];//分配12个空间,存储字段名 s = string.Empty; Array.Copy(buf, filed.name, 11); int index = Array.IndexOf(filed.name, (byte)0x0); //把'\0'后面的字符都至为'\0' if (index != -1) for (int j = index; j < 12; j++) filed.name[j] = 0; filed.type = buf[11]; filed.addr = y; filed.width = buf[16]; y += filed.width; filed.deC = buf[17]; if (Encoding.GetEncoding(936).GetString(filed.name).Trim("\0".ToCharArray()) == "") break; fields.Add(Encoding.GetEncoding(936).GetString(filed.name).Trim("\0".ToCharArray()).ToUpper(), filed); } } //关闭文件 public void close() { file.Close(); } /// <summary> /// 读取DBF的头定义字节内容 /// </summary> /// <returns></returns> public byte[] GetHeads() { byte[] data = new byte[HeadLen]; file.Seek(offset, SeekOrigin.Begin); file.Read(data, 0, HeadLen); return data; } /// <summary> /// 获取要读取的行对象列表 /// </summary> /// <param name="BeginRow">开始行,行号从0开始</param> /// <returns></returns> public List<DBFDataRow> Rows(int BeginRow = 0) { List<DBFDataRow> rs = new List<DBFDataRow>(); file.Seek(HeadLen + offset + RowLen * BeginRow, SeekOrigin.Begin); int p = BeginRow, r; while (p < RowsCount) { byte[] bytes = new byte[RowLen]; r = file.Read(bytes, 0, RowLen); if (r < RowLen) p = RowsCount; else { rs.Add(new DBFDataRow(bytes, fields)); p++; } } return rs; } /// <summary> /// 读取指定行指定字段值 /// </summary> /// <param name="row">行号</param> /// <param name="column">列字段名</param> /// <returns></returns> public string FiledsAsString(int row, string column) { byte[] data = new byte[fields[column].width]; //文件指针移向字段的数据地址处 file.Seek(HeadLen + RowLen * row + fields[column.ToUpper()].addr + offset, SeekOrigin.Begin); file.Read(data, 0, data.Length); return Encoding.GetEncoding(936).GetString(data).Trim("\0".ToCharArray()).Trim(); } //一次性获取DBF内容 public byte[] GetAllRows() { byte[] bytes = new byte[RowsCount * RowLen]; file.Seek(HeadLen + offset, SeekOrigin.Begin); file.Read(bytes, 0, bytes.Length); return bytes; } /// <summary> /// 获取dbf行数据 /// </summary> /// <param name="bStart"></param> /// <param name="rows"></param> /// <returns></returns> public byte[] GetRows(bool bStart, int rows) { byte[] bytes = new byte[rows * RowLen]; if (bStart) file.Seek(HeadLen + offset, SeekOrigin.Begin); file.Read(bytes, 0, bytes.Length); return bytes; } /// <summary> /// 一行一行获取dbf行数据 /// </summary> /// <param name="bStart"></param> /// <param name="rows"></param> /// <returns></returns> public IEnumerable<byte[]> GetEnumerableRows() { file.Seek(HeadLen + offset, SeekOrigin.Begin); for (int i = 0; i < RowsCount; i++) { byte[] bytes = new byte[RowLen]; file.Read(bytes, 0, bytes.Length); yield return bytes; } } /// <summary> /// 指定行是否已经打了删除标记 /// </summary> /// <param name="row">行号</param> /// <returns>true:已经打了删除标记, false:未打删除标记</returns> public bool RowHasDeleted(int row) { file.Seek(offset + HeadLen + RowLen * row, SeekOrigin.Begin); if (file.ReadByte() == 42) return true; else return false; } /// <summary> /// 导出DataTable /// </summary> /// <returns></returns> public DataTable DBFToDataTable() { DataTable dt = new DataTable(); foreach (var kvp in fields) { if ((char)kvp.Value.type == 'D') dt.Columns.Add(new DataColumn(kvp.Key, typeof(DateTime))); else if ((char)kvp.Value.type == 'N') dt.Columns.Add(new DataColumn(kvp.Key, typeof(double))); else dt.Columns.Add(new DataColumn(kvp.Key, typeof(string))); } file.Seek(HeadLen + offset, SeekOrigin.Begin); for (int i = 0; i < RowsCount; i++) { try { var dr = dt.NewRow(); //读取行开头字节 var tmp = new byte[1]; file.Read(tmp, 0, 1); foreach (DataColumn col in dt.Columns) { byte[] data = new byte[fields[col.ColumnName].width]; file.Read(data, 0, data.Length); if (col.DataType == typeof(DateTime)) dr[col.ColumnName] = StringToDate(Encoding.GetEncoding(936).GetString(data).Trim("\0".ToCharArray()).Trim()); else dr[col.ColumnName] = Encoding.GetEncoding(936).GetString(data).Trim("\0".ToCharArray()).Trim(); } dt.Rows.Add(dr); } catch { } } return dt; } public static DateTime StringToDate(string s) { return DateTime.ParseExact(s, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture); } } public enum NativeDbType : byte { //Autoincrement = (byte)0x2B, //+ in ASCII //Timestamp = (byte)0x40, //@ in ASCII //Binary = (byte)0x42, //B in ASCII //Long = (byte)0x49, //I in ASCII //Logical = (byte)0x4C, //L in ASCII //Memo = (byte)0x4D, //M in ASCII //Ole = (byte)0x47, //G in ASCII //Float = (byte)0x46, //F in ASCII //Double = (byte)0x4F, //O in ASCII Char = (byte)0x43, //C in ASCII Date = (byte)0x44, //D in ASCII 8字节 Numeric = (byte)0x4E, //N in ASCII } public class DBFField { public string Name { get; private set; } public int FieldLength { get; private set; } public NativeDbType dataType { get; private set; } public int DecimalCount { get; private set; } public string TabFieldName { get; private set; } /// <summary> /// 构建字段 /// </summary> /// <param name="aFieldName"></param> /// <param name="aType"></param> /// <param name="aFieldLength"></param> /// <param name="aDecimalCount"></param> public DBFField(string aFieldName, NativeDbType aType, Int32 aFieldLength = 0, Int32 aDecimalCount = 0) { Name = aFieldName; TabFieldName = aFieldName; dataType = aType; FieldLength = aFieldLength; DecimalCount = aDecimalCount; if (dataType == NativeDbType.Char && FieldLength <= 0) throw new Exception("字符串字段长度必须大于0"); if (dataType == NativeDbType.Numeric && FieldLength <= 0) throw new Exception("数值字段长度必须大于0"); if (dataType == NativeDbType.Numeric && DecimalCount < 0) throw new Exception("数值字段精度必须大于或等于0"); if (dataType == NativeDbType.Date) FieldLength = 8; } /// <summary> /// /// </summary> /// <param name="aFieldName">dbf字段名</param> /// <param name="aType">字段类型</param> /// <param name="aSourFieldName">datatable中的字段名</param> /// <param name="aFieldLength">字段长度</param> /// <param name="aDecimalCount">小数位数</param> public DBFField(string aFieldName, NativeDbType aType, string aSourFieldName, Int32 aFieldLength = 0, Int32 aDecimalCount = 0) { Name = aFieldName; TabFieldName = aSourFieldName; dataType = aType; FieldLength = aFieldLength; DecimalCount = aDecimalCount; if (dataType == NativeDbType.Char && FieldLength <= 0) throw new Exception("字符串字段长度必须大于0"); if (dataType == NativeDbType.Numeric && FieldLength <= 0) throw new Exception("数值字段长度必须大于0"); if (dataType == NativeDbType.Numeric && DecimalCount < 0) throw new Exception("数值字段精度必须大于或等于0"); if (dataType == NativeDbType.Date) FieldLength = 8; } } public class DBFExport { /// <summary> /// 导出dbf文件 /// </summary> /// <param name="path"></param> /// <param name="dtSource"></param> public static void DataTableToDBF(string path, DataTable dtSource, DBFField[] Fields) { if (dtSource == null) throw new Exception("DBF导出数据源不可为空"); if (Fields == null) throw new Exception("DBF导出字段不可为空"); using (var fm = new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) { using (var bw = new BinaryWriter(fm)) { //文件头 bw.Write((byte)3); bw.Write((byte)(DateTime.Now.Year % 100)); bw.Write((byte)DateTime.Now.Month); bw.Write((byte)DateTime.Now.Day); bw.Write(dtSource.Rows.Count); bw.Write((Int16)(Fields.Length * 32 + 33)); //计算一条总字节数记录 int recordBytiesCnt = 0; foreach (var field in Fields) recordBytiesCnt += field.FieldLength; bw.Write((Int16)(recordBytiesCnt + 1)); bw.Write(new byte[20]); //字段属性 int fieldBeginPos = 1; foreach (var field in Fields) { var fieldNameBytes = Encoding.GetEncoding(936).GetBytes(field.Name); if (fieldNameBytes.Length > 11) { var dest = new byte[11]; Array.Copy(fieldNameBytes, dest, 11); fieldNameBytes = dest; } if (fieldNameBytes.Length < 11) { var dest = new byte[11]; Array.Copy(fieldNameBytes, dest, fieldNameBytes.Length); fieldNameBytes = dest; } bw.Write(fieldNameBytes); bw.Write((byte)field.dataType); bw.Write((byte)fieldBeginPos); bw.Write(new byte[3]); bw.Write((byte)field.FieldLength); bw.Write((byte)field.DecimalCount); bw.Write(new byte[14]); fieldBeginPos += field.FieldLength; } bw.Write((byte)0x0D); //数据体 foreach (DataRow dr in dtSource.Rows) { bw.Write((byte)0x20); foreach (var field in Fields) bw.Write(GetBytesFromField(dr[field.TabFieldName], field)); } } } } private static byte[] GetBytesFromField(object value, DBFField field) { byte[] res = { 0x20 }; if (value != null && value != DBNull.Value && !string.IsNullOrEmpty(value.ToString())) { if (field.dataType == NativeDbType.Date) res = Encoding.GetEncoding(936).GetBytes(Convert.ToDateTime(value).ToString("yyyyMMdd")); else if (field.dataType == NativeDbType.Numeric) { string svalue = Convert.ToDecimal(value).ToString(); int ipos = svalue.IndexOf('.'); if (ipos > 0 && field.DecimalCount == 0) svalue = svalue.Substring(0, ipos); else if (field.DecimalCount > 0) { if (ipos < 0) svalue += ".".PadRight(field.DecimalCount + 1, '0'); else if (svalue.Length - ipos - 1 < field.DecimalCount) //实际小数位数小于要求位数 svalue += "0".PadRight(field.DecimalCount - (svalue.Length - ipos - 1)); else if (svalue.Length - ipos - 1 > field.DecimalCount) //实际小数位数大于要求位数 svalue = svalue.Remove(ipos + field.DecimalCount + 1); } res = Encoding.GetEncoding(936).GetBytes(svalue); } else res = Encoding.GetEncoding(936).GetBytes(value.ToString()); } var dest = new byte[field.FieldLength]; if (res.Length == field.FieldLength) return res; else if (res.Length > field.FieldLength) Array.Copy(res, dest, field.FieldLength); else { if (field.dataType == NativeDbType.Numeric) { //前填空格 for (int i = 0; i < field.FieldLength - res.Length; i++) dest[i] = 0x20; Array.Copy(res, 0, dest, field.FieldLength - res.Length, res.Length); } else { Array.Copy(res, dest, res.Length); //后填空格 for (int i = res.Length; i < field.FieldLength; i++) dest[i] = 0x20; } } return dest; } }
使用
/// <summary> /// 读DBF文件到DataTable /// </summary> /// <param name="dbname">DBF实际绝对路径带文件名</param> /// <returns></returns> public static DataTable DbfToDataTable(string dbname) { try { DBFPhaser DbfHelp = new DBFPhaser(dbname); DataTable DbfDt = DbfHelp.DBFToDataTable(); return DbfDt; } catch (Exception ex) { throw new Exception(ex.Message); } }
本文来自博客园,作者:无产铁锤,转载请注明原文链接:https://www.cnblogs.com/wzhsun/p/17629610.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」