C# 讀取DBF文件到Datatable


      此種方式不依賴與任何驅動,第三方插件

核心代碼TDbfTable如下:

     

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;

namespace TestSCClearCard
{
    #region TDbfTable更新歷史
    //-----------------------------------------------------------------------------------------------
    // 1) 2013-06-10: (1) 自 ReadDbf.cs 移植過來
    //                (2) 讀記錄時, 首先讀刪除標志字節, 然后讀記錄. 原代碼這個地方忽略了刪除標志字節.
    //                (3) 原代碼 MoveNext() 后沒有再判是否文件尾, 在讀修改結構后的表時報錯誤.
    // 2) 2013-06-11: (1) int.money.decimal.double 直接用 BitConverter 轉換函數
    //                (2) Date類型轉換時, 如果字符串為空, 則為1899-12-30日.
    //                (3) Time類型轉換時, 如果天數和毫秒數全為0, 則為1899-12-30日.
    //                (4) Time類型轉換時, 毫秒數存在誤差, 有1000以下的整數數, 需要+1秒.
    //                (5) 讀記錄值時,  在定位首指針后, 順序讀每個記錄數組.
    // 3) 2013-06-17  (1) 打開文件后需要關閉文件流, 增加一個 CloseFileStream() 方法
    // 4) 2013-06-18  (1) 把 CloseFileStream() 放到 try{}finally{} 結構中
    //-----------------------------------------------------------------------------------------------------
    #endregion

    public class TDbfHeader
    {
        public const int HeaderSize = 32;
        public sbyte Version;
        public byte LastModifyYear;
        public byte LastModifyMonth;
        public byte LastModifyDay;
        public int 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];
    }

    public class TDbfField
    {
        public const int FieldSize = 32;
        public byte[] NameBytes = new byte[11];  // 字段名稱
        public byte TypeChar;
        public byte Length;
        public byte Precision;
        public byte[] Reserved = new byte[2];
        public sbyte DbaseivID;
        public byte[] Reserved2 = new byte[10];
        public sbyte ProductionIndex;

        public bool IsString {
            get {
                if (TypeChar == 'C')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsMoney {
            get {
                if (TypeChar == 'Y')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsNumber {
            get {
                if (TypeChar == 'N')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsFloat {
            get {
                if (TypeChar == 'F')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsDate {
            get {
                if (TypeChar == 'D')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsTime {
            get {
                if (TypeChar == 'T')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsDouble {
            get {
                if (TypeChar == 'B')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsInt {
            get {
                if (TypeChar == 'I')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsLogic {
            get {
                if (TypeChar == 'L')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsMemo {
            get {
                if (TypeChar == 'M')
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsGeneral {
            get {
                if (TypeChar == 'G')
                {
                    return true;
                }

                return false;
            }
        }

        public Type FieldType {
            get {
                if (this.IsString == true)
                {
                    return typeof(string);
                }
                else if (this.IsMoney == true || this.IsNumber == true || this.IsFloat == true)
                {
                    return typeof(decimal);
                }
                else if (this.IsDate == true || this.IsTime == true)
                {
                    return typeof(System.DateTime);
                }
                else if (this.IsDouble == true)
                {
                    return typeof(double);
                }
                else if (this.IsInt == true)
                {
                    return typeof(System.Int32);
                }
                else if (this.IsLogic == true)
                {
                    return typeof(bool);
                }
                else if (this.IsMemo == true)
                {
                    return typeof(string);
                }
                else if (this.IsMemo == true)
                {
                    return typeof(string);
                }
                else
                {
                    return typeof(string);
                }
            }
        }

        public string GetFieldName()
        {
            return GetFieldName(System.Text.Encoding.Default);
        }

        public string GetFieldName(System.Text.Encoding encoding)
        {
            string fieldName = encoding.GetString(NameBytes);
            int i = fieldName.IndexOf('\0');
            if (i > 0)
            {
                return fieldName.Substring(0, i).Trim();
            }

            return fieldName.Trim();
        }
    }

    public class TDbfTable : IDisposable
    {
        private const byte DeletedFlag = 0x2A;
        private DateTime NullDateTime = new DateTime(1899, 12, 30);  // odbc中空日期對應的轉換日期

        private string _dbfFileName = null;

        private System.Text.Encoding _encoding = System.Text.Encoding.Default;
        private System.IO.FileStream _fileStream = null;
        private System.IO.BinaryReader _binaryReader = null;

        private bool _isFileOpened;
        private byte[] _recordBuffer;
        private int _fieldCount = 0;

        private TDbfHeader _dbfHeader = null;
        private TDbfField[] _dbfFields;
        private System.Data.DataTable _dbfTable = null;

        public TDbfTable(string fileName)
        {
            this._dbfFileName = fileName.Trim();
            try
            {
                this.OpenDbfFile();
            }
            finally
            {
                this.CloseFileStream();
            }
        }

        public TDbfTable(string fileName, string encodingName)
        {
            this._dbfFileName = fileName.Trim();
            this._encoding = GetEncoding(encodingName);
            try
            {
                this.OpenDbfFile();
            }
            finally
            {
                this.CloseFileStream();
            }
        }

        void System.IDisposable.Dispose()
        {
            this.Dispose(true);  // TODO:  添加 DBFFile.System.IDisposable.Dispose 實現
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing == true)
            {
                this.Close();
            }
        }

        private System.Text.Encoding GetEncoding(string encodingName)
        {
            if (string.IsNullOrEmpty(encodingName) == true)
            {
                return System.Text.Encoding.Default;
            }

            if (encodingName.ToUpper() == "GB2313")
            {
                return System.Text.Encoding.GetEncoding("GB2312");
            }

            if (encodingName.ToUpper() == "UNICODE")
            {
                return System.Text.Encoding.Unicode;
            }

            if (encodingName.ToUpper() == "UTF8")
            {
                return System.Text.Encoding.UTF8;
            }

            if (encodingName.ToUpper() == "UTF7")
            {
                return System.Text.Encoding.UTF7;
            }

            if (encodingName.ToUpper() == "UTF32")
            {
                return System.Text.Encoding.UTF32;
            }

            if (encodingName.ToUpper() == "ASCII")
            {
                return System.Text.Encoding.ASCII;
            }

            return System.Text.Encoding.Default;
        }

        public void Close()
        {
            this.CloseFileStream();

            _recordBuffer = null;
            _dbfHeader = null;
            _dbfFields = null;

            _isFileOpened = false;
            _fieldCount = 0;
        }

        private void CloseFileStream()
        {
            if (_fileStream != null)
            {
                _fileStream.Close();
                _fileStream = null;
            }

            if (_binaryReader != null)
            {
                _binaryReader.Close();
                _binaryReader = null;
            }
        }

        private void OpenDbfFile()
        {
            this.Close();

            if (string.IsNullOrEmpty(_dbfFileName) == true)
            {
                throw new Exception("filename is empty or null.");
            }

            if (System.IO.File.Exists(_dbfFileName) == false)
            {
                throw new Exception(this._dbfFileName + " does not exist.");
            }

            try
            {
                this.GetFileStream();
                this.ReadHeader();
                this.ReadFields();
                this.GetRecordBufferBytes();
                this.CreateDbfTable();
                this.GetDbfRecords();
            }
            catch (Exception e)
            {
                this.Close();
                throw e;
            }
        }

        public void GetFileStream()
        {
            try
            {
                this._fileStream = File.Open(this._dbfFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                this._binaryReader = new BinaryReader(this._fileStream, _encoding);
                this._isFileOpened = true;
            }
            catch
            {
                throw new Exception("fail to read  " + this._dbfFileName + ".");
            }
        }

        private void ReadHeader()
        {
            this._dbfHeader = new TDbfHeader();

            try
            {
                this._dbfHeader.Version = this._binaryReader.ReadSByte();         //第1字節
                this._dbfHeader.LastModifyYear = this._binaryReader.ReadByte();   //第2字節
                this._dbfHeader.LastModifyMonth = this._binaryReader.ReadByte();  //第3字節
                this._dbfHeader.LastModifyDay = this._binaryReader.ReadByte();    //第4字節
                this._dbfHeader.RecordCount = this._binaryReader.ReadInt32();     //第5-8字節
                this._dbfHeader.HeaderLength = this._binaryReader.ReadUInt16();   //第9-10字節
                this._dbfHeader.RecordLength = this._binaryReader.ReadUInt16();   //第11-12字節
                this._dbfHeader.Reserved = this._binaryReader.ReadBytes(16);      //第13-14字節
                this._dbfHeader.TableFlag = this._binaryReader.ReadSByte();       //第15字節
                this._dbfHeader.CodePageFlag = this._binaryReader.ReadSByte();    //第16字節
                this._dbfHeader.Reserved2 = this._binaryReader.ReadBytes(2);      //第17-18字節

                this._fieldCount = GetFieldCount();
            }
            catch
            {
                throw new Exception("fail to read file header.");
            }
        }

        private int GetFieldCount()
        {
            // 由於有些dbf文件的文件頭最后有附加區段,但是有些文件沒有,在此使用笨方法計算字段數目
            // 就是測試每一個存儲字段結構區域的第一個字節的值,如果不為0x0D,表示存在一個字段
            // 否則從此處開始不再存在字段信息

            int fCount = (this._dbfHeader.HeaderLength - TDbfHeader.HeaderSize - 1) / TDbfField.FieldSize;

            for (int k = 0; k < fCount; k++)
            {
                _fileStream.Seek(TDbfHeader.HeaderSize + k * TDbfField.FieldSize, SeekOrigin.Begin);  // 定位到每個字段結構區,獲取第一個字節的值
                byte flag = this._binaryReader.ReadByte();

                if (flag == 0x0D)  // 如果獲取到的標志不為0x0D,則表示該字段存在;否則從此處開始后面再沒有字段信息
                {
                    return k;
                }
            }

            return fCount;
        }

        private void ReadFields()
        {
            _dbfFields = new TDbfField[_fieldCount];

            try
            {
                _fileStream.Seek(TDbfHeader.HeaderSize, SeekOrigin.Begin);
                for (int k = 0; k < _fieldCount; k++)
                {
                    this._dbfFields[k] = new TDbfField();
                    this._dbfFields[k].NameBytes = this._binaryReader.ReadBytes(11);
                    this._dbfFields[k].TypeChar = this._binaryReader.ReadByte();

                    this._binaryReader.ReadBytes(4);  // 保留, 源代碼是讀 UInt32()給 Offset

                    this._dbfFields[k].Length = this._binaryReader.ReadByte();
                    this._dbfFields[k].Precision = this._binaryReader.ReadByte();
                    this._dbfFields[k].Reserved = this._binaryReader.ReadBytes(2);
                    this._dbfFields[k].DbaseivID = this._binaryReader.ReadSByte();
                    this._dbfFields[k].Reserved2 = this._binaryReader.ReadBytes(10);
                    this._dbfFields[k].ProductionIndex = this._binaryReader.ReadSByte();
                }
            }
            catch
            {
                throw new Exception("fail to read field information.");
            }
        }

        private void GetRecordBufferBytes()
        {
            this._recordBuffer = new byte[this._dbfHeader.RecordLength];

            if (this._recordBuffer == null)
            {
                throw new Exception("fail to allocate memory .");
            }
        }

        private void CreateDbfTable()
        {
            if (_dbfTable != null)
            {
                _dbfTable.Clear();
                _dbfTable = null;
            }

            _dbfTable = new System.Data.DataTable();
            _dbfTable.TableName = this.TableName;

            for (int k = 0; k < this._fieldCount; k++)
            {
                System.Data.DataColumn col = new System.Data.DataColumn();
                string colText = this._dbfFields[k].GetFieldName(_encoding);

                if (string.IsNullOrEmpty(colText) == true)
                {
                    throw new Exception("the " + (k + 1) + "th column name is null.");
                }

                col.ColumnName = colText;
                col.Caption = colText;
                col.DataType = this._dbfFields[k].FieldType;
                _dbfTable.Columns.Add(col);
            }
        }

        public void GetDbfRecords()
        {
            try
            {
                this._fileStream.Seek(this._dbfHeader.HeaderLength, SeekOrigin.Begin);

                for (int k = 0; k < this.RecordCount; k++)
                {
                    if (ReadRecordBuffer(k) != DeletedFlag)
                    {
                        System.Data.DataRow row = _dbfTable.NewRow();
                        for (int i = 0; i < this._fieldCount; i++)
                        {
                            row[i] = this.GetFieldValue(i);
                        }
                        _dbfTable.Rows.Add(row);
                    }
                }
            }
            catch (ArgumentOutOfRangeException e)
            {
                throw e;
            }
            catch
            {
                throw new Exception("fail to get dbf table.");
            }
        }

        private byte ReadRecordBuffer(int recordIndex)
        {
            byte deleteFlag = this._binaryReader.ReadByte();  // 刪除標志
            this._recordBuffer = this._binaryReader.ReadBytes(this._dbfHeader.RecordLength - 1);  // 標志位已經讀取
            return deleteFlag;
        }

        private string GetFieldValue(int fieldIndex)
        {
            string fieldValue = null;

            int offset = 0;
            for (int i = 0; i < fieldIndex; i++)
            {
                offset += _dbfFields[i].Length;
            }

            byte[] tmp = CopySubBytes(this._recordBuffer, offset, this._dbfFields[fieldIndex].Length);

            if (this._dbfFields[fieldIndex].IsInt == true)
            {
                int val = System.BitConverter.ToInt32(tmp, 0);
                fieldValue = val.ToString();
            }
            else if (this._dbfFields[fieldIndex].IsDouble == true)
            {
                double val = System.BitConverter.ToDouble(tmp, 0);
                fieldValue = val.ToString();
            }
            else if (this._dbfFields[fieldIndex].IsMoney == true)
            {
                long val = System.BitConverter.ToInt64(tmp, 0);  // 將字段值放大10000倍,變成long型存儲,然后縮小10000倍。
                fieldValue = ((decimal)val / 10000).ToString();
            }
            else if (this._dbfFields[fieldIndex].IsDate == true)
            {
                DateTime date = ToDate(tmp);
                fieldValue = date.ToString();

            }
            else if (this._dbfFields[fieldIndex].IsTime == true)
            {
                DateTime time = ToTime(tmp);
                fieldValue = time.ToString();

            }
            else
            {
                fieldValue = this._encoding.GetString(tmp);
            }

            fieldValue = fieldValue.Trim();

            // 如果本子段類型是數值相關型,進一步處理字段值
            if (this._dbfFields[fieldIndex].IsNumber == true || this._dbfFields[fieldIndex].IsFloat == true)    // N - 數值型, F - 浮點型                    
            {
                if (fieldValue.Length == 0)
                {
                    fieldValue = "0";
                }
                else if (fieldValue == ".")
                {
                    fieldValue = "0";
                }
                else
                {
                    decimal val = 0;

                    if (decimal.TryParse(fieldValue, out val) == false)  // 將字段值先轉化為Decimal類型然后再轉化為字符串型,消除類似“.000”的內容, 如果不能轉化則為0
                    {
                        val = 0;
                    }

                    fieldValue = val.ToString();
                }
            }
            else if (this._dbfFields[fieldIndex].IsLogic == true)    // L - 邏輯型
            {
                if (fieldValue != "T" && fieldValue != "Y")
                {
                    fieldValue = "false";
                }
                else
                {
                    fieldValue = "true";
                }
            }
            else if (this._dbfFields[fieldIndex].IsDate == true || this._dbfFields[fieldIndex].IsTime == true)   // D - 日期型  T - 日期時間型                    
            {
                // 暫時不做任何處理
            }

            return fieldValue;
        }

        private static byte[] CopySubBytes(byte[] buf, int startIndex, long length)
        {
            if (startIndex >= buf.Length)
            {
                throw new ArgumentOutOfRangeException("startIndex");
            }

            if (length == 0)
            {
                throw new ArgumentOutOfRangeException("length", "length must be great than 0.");
            }

            if (length > buf.Length - startIndex)
            {
                length = buf.Length - startIndex;  // 子數組的長度超過從startIndex起到buf末尾的長度時,修正為剩余長度
            }

            byte[] target = new byte[length];
            Array.Copy(buf, startIndex, target, 0, length);
            return target;
        }

        private DateTime ToDate(byte[] buf)
        {
            if (buf.Length != 8)
            {
                throw new ArgumentException("date array length must be 8.", "buf");
            }

            string dateStr = System.Text.Encoding.ASCII.GetString(buf).Trim();
            if (dateStr.Length < 8)
            {
                return NullDateTime;
            }

            int year = int.Parse(dateStr.Substring(0, 4));
            int month = int.Parse(dateStr.Substring(4, 2));
            int day = int.Parse(dateStr.Substring(6, 2));

            return new DateTime(year, month, day);
        }

        private DateTime ToTime(byte[] buf)
        {
            if (buf.Length != 8)
            {
                throw new ArgumentException("time array length must be 8.", "buf");
            }

            try
            {
                byte[] tmp = CopySubBytes(buf, 0, 4);
                tmp.Initialize();
                int days = System.BitConverter.ToInt32(tmp, 0);  // ( ToInt32(tmp); // 獲取天數                

                tmp = CopySubBytes(buf, 4, 4);  // 獲取毫秒數
                int milliSeconds = System.BitConverter.ToInt32(tmp, 0);  // ToInt32(tmp);

                if (days == 0 && milliSeconds == 0)
                {
                    return NullDateTime;
                }

                int seconds = milliSeconds / 1000;
                int milli = milliSeconds % 1000;  // vfp實際上沒有毫秒級, 是秒轉換來的, 測試時發現2秒鍾轉換為1999毫秒的情況
                if (milli > 0)
                {
                    seconds += 1;
                }

                DateTime date = DateTime.MinValue;  // 在最小日期時間的基礎上添加剛獲取的天數和秒數,得到日期字段數值
                date = date.AddDays(days - 1721426);
                date = date.AddSeconds(seconds);

                return date;
            }
            catch
            {
                return new DateTime();
            }
        }

        public string TableName {
            get { return System.IO.Path.GetFileNameWithoutExtension(this._dbfFileName); }
        }

        public System.Text.Encoding Encoding {
            get { return this._encoding; }
        }

        public int RecordLength {
            get {
                if (this.IsFileOpened == false)
                {
                    return 0;
                }

                return this._dbfHeader.RecordLength;
            }
        }

        public int FieldCount {
            get {
                if (this.IsFileOpened == false)
                {
                    return 0;
                }

                return this._dbfFields.Length;
            }
        }

        public int RecordCount {
            get {
                if (this.IsFileOpened == false || this._dbfHeader == null)
                {
                    return 0;
                }

                return this._dbfHeader.RecordCount;
            }
        }

        public bool IsFileOpened {
            get {
                return this._isFileOpened;
            }
        }

        public System.Data.DataTable Table {
            get {
                if (_isFileOpened == false)
                {
                    return null;
                }
                return _dbfTable;
            }
        }

        public TDbfField[] DbfFields {
            get {
                if (_isFileOpened == false)
                {
                    return null;
                }

                return _dbfFields;
            }
        }

        public static implicit operator DataTable(TDbfTable v)
        {
            throw new NotImplementedException();
        }
    }
}

  使用說明:

  1. TestSCClearCard.TDbfTable dbf = new TestSCClearCard.TDbfTable(dbf文件名),其中dbf文件是含路徑的全文件名
  2. dbf.Table即是獲取的DataTable對象;
  3. dbf.DbfFields為DBF字段數組。

     調用:

  string dbfPath = System.Windows.Forms.Application.StartupPath + @"\cbk.dbf";
  TestSCClearCard.TDbfTable dbf = new TestSCClearCard.TDbfTable(dbfPath);
  DataTable dt = dbf.Table;

  結果:

 

 

 

 原文資料出自:一個C#讀取DBF文件的類TDbfTable

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM