主頁 >  其他 > .NET使用FastDBF讀寫DBF

.NET使用FastDBF讀寫DBF

2020-10-17 11:15:30 其他

FastDBF源代碼地址:https://github.com/SocialExplorer/FastDBF

第一步在解決方案中新建一個類別庫的專案:取名為SocialExplorer.FastDBF

 

 

 

第二步:引入FASTDBF的源檔案  源代碼可以通過github地址下載引入

 

源檔案:DbfColumn.cs

///
/// Author: Ahmed Lacevic
/// Date: 12/1/2007
/// 
/// Revision History:
/// -----------------------------------
///   Author:
///   Date:
///   Desc:

using System;
using System.Collections.Generic;
using System.Text;


namespace SocialExplorer.IO.FastDBF
{ 
  
  /// <summary>
  /// This class represents a DBF Column.
  /// </summary>
  /// 
  /// <remarks>
  /// Note that certain properties can not be modified after creation of the object. 
  /// This is because we are locking the header object after creation of a data row,
  /// and columns are part of the header so either we have to have a lock field for each column,
  /// or make it so that certain properties such as length can only be set during creation of a column.
  /// Otherwise a user of this object could modify a column that belongs to a locked header and thus corrupt the DBF file.
  /// </remarks>
  public class DbfColumn : ICloneable
  { 
    
    /*
     (FoxPro/FoxBase) Double integer *NOT* a memo field
     G     General     (dBASE V: like Memo) OLE Objects in MS Windows versions 
     P     Picture     (FoxPro) Like Memo fields, but not for text processing. 
     Y     Currency     (FoxPro)
     T     DateTime     (FoxPro)
     I     Integer     Length: 4 byte little endian integer     (FoxPro)
    */
    
    /// <summary>
    ///  Great information on DBF located here: 
    ///  http://www.clicketyclick.dk/databases/xbase/format/data_types.html
    ///  http://www.clicketyclick.dk/databases/xbase/format/dbf.html
    /// </summary>
    public enum DbfColumnType
    { 
      
      /// <summary>
      /// Character  less than 254 length
      /// ASCII text less than 254 characters long in dBASE. 
      /// 
      /// Character fields can be up to 32 KB long (in Clipper and FoxPro) using decimal 
      /// count as high byte in field length. It's possible to use up to 64KB long fields 
      /// by reading length as unsigned.
      /// 
      /// </summary>
      Character = 0,
      
      /// <summary>
      /// Number     Length: less than 18 
      ///   ASCII text up till 18 characters long (include sign and decimal point). 
      /// 
      /// Valid characters: 
      ///    "0" - "9" and "-". Number fields can be up to 20 characters long in FoxPro and Clipper. 
      /// </summary>
      /// <remarks>
      /// We are not enforcing this 18 char limit.
      /// </remarks>
      Number = 1,
      
      /// <summary>
      ///  L  Logical  Length: 1    Boolean/byte (8 bit) 
      ///  
      ///  Legal values: 
      ///   ?     Not initialised (default)
      ///   Y,y     Yes
      ///   N,n     No
      ///   F,f     False
      ///   T,t     True
      ///   Logical fields are always displayed using T/F/?. Some sources claims 
      ///   that space (ASCII 20h) is valid for not initialised. Space may occur, but is not defined.      
      /// </summary>
      Boolean = 2,
      
      /// <summary>
      /// D     Date     Length: 8  Date in format YYYYMMDD. A date like 0000-00- 00 is *NOT* valid. 
      /// </summary>
      Date = 3,
      
      /// <summary>
      /// M     Memo     Length: 10     Pointer to ASCII text field in memo file 10 digits representing a pointer to a DBT block (default is blanks). 
      /// </summary>
      Memo = 4,
      
      /// <summary>
      /// B     Binary          (dBASE V) Like Memo fields, but not for text processing.
      /// </summary>
      Binary = 5,
      
      /// <summary>
      /// I     Integer     Length: 4 byte little endian integer     (FoxPro)
      /// </summary>
      Integer = 6, 
      
    }
    
    
    /// <summary>
    /// Column (field) name
    /// </summary>
    private string mName;
    
    
    /// <summary>
    /// Field Type (Char, number, boolean, date, memo, binary)
    /// </summary>
    private DbfColumnType mType;
    
    
    /// <summary>
    /// Offset from the start of the record
    /// </summary>
    internal int mDataAddress;
    
    
    /// <summary>
    /// Length of the data in bytes; some rules apply which are in the spec (read more above).
    /// </summary>
    private int mLength;
    
    
    /// <summary>
    /// Decimal precision count, or number of digits afer decimal point. This applies to Number types only.
    /// </summary>
    private int mDecimalCount;
    
    
    
    /// <summary>
    /// Full spec constructor sets all relevant fields.
    /// </summary>
    /// <param name="sName"></param>
    /// <param name="type"></param>
    /// <param name="nLength"></param>
    /// <param name="nDecimals"></param>
    public DbfColumn(string sName, DbfColumnType type, int nLength, int nDecimals)
    { 
      
      Name = sName;
      mType = type;
      mLength = nLength;
      
      if(type == DbfColumnType.Number)
        mDecimalCount = nDecimals;
      else
        mDecimalCount = 0;
      
      
      
      //perform some simple integrity checks...
      //-------------------------------------------
      
      //decimal precision:
      //we could also fix the length property with a statement like this: mLength = mDecimalCount + 2;
      //lyq修改原始碼取消判斷
      //if (mDecimalCount > 0 && mLength - mDecimalCount <= 1)
      //  throw new Exception("Decimal precision can not be larger than the length of the field.");
      
      if(mType == DbfColumnType.Integer)
        mLength = 4;
      
      if(mType == DbfColumnType.Binary)
        mLength = 1;
      
      if(mType == DbfColumnType.Date)
        mLength = 8;  //Dates are exactly yyyyMMdd
      
      if(mType == DbfColumnType.Memo)
        mLength = 10;  //Length: 10 Pointer to ASCII text field in memo file. pointer to a DBT block.
      
      if(mType == DbfColumnType.Boolean)
        mLength = 1;
      
      //field length:
      if (mLength <= 0)
        throw new Exception("Invalid field length specified. Field length can not be zero or less than zero.");
      else if (type != DbfColumnType.Character && type != DbfColumnType.Binary && mLength > 255)
        throw new Exception("Invalid field length specified. For numbers it should be within 20 digits, but we allow up to 255. For Char and binary types, length up to 65,535 is allowed. For maximum compatibility use up to 255.");
      else if((type == DbfColumnType.Character || type == DbfColumnType.Binary) && mLength > 65535)
        throw new Exception("Invalid field length specified. For Char and binary types, length up to 65535 is supported. For maximum compatibility use up to 255.");
      
      
    }
    
    
    /// <summary>
    /// Create a new column fully specifying all properties.
    /// </summary>
    /// <param name="sName">column name</param>
    /// <param name="type">type of field</param>
    /// <param name="nLength">field length including decimal places and decimal point if any</param>
    /// <param name="nDecimals">decimal places</param>
    /// <param name="nDataAddress">offset from start of record</param>
    internal DbfColumn(string sName, DbfColumnType type, int nLength, int nDecimals, int nDataAddress): this(sName, type, nLength, nDecimals)
    { 
      
      mDataAddress = nDataAddress;
      
    }
    
    
    public DbfColumn(string sName, DbfColumnType type): this(sName, type, 0, 0)
    { 
      if(type == DbfColumnType.Number || type == DbfColumnType.Character )
        throw new Exception("For number and character field types you must specify Length and Decimal Precision.");
      
    }
    
    
    /// <summary>
    /// Field Name.
    /// </summary>
    public string Name
    { 
      get
      { 
        return mName;
      }
      
      set
      { 
        //name:
        if (string.IsNullOrEmpty(value))
          throw new Exception("Field names must be at least one char long and can not be null.");
        
        if (value.Length > 11)
          throw new Exception("Field names can not be longer than 11 chars.");
        
        mName = value;
        
      }
      
    }
    
    
    /// <summary>
    /// Field Type (C N L D or M).
    /// </summary>
    public DbfColumnType ColumnType
    { 
      get
      { 
        return mType;
      }
    }
    
    
    /// <summary>
    /// Returns column type as a char, (as written in the DBF column header)
    /// N=number, C=char, B=binary, L=boolean, D=date, I=integer, M=memo
    /// </summary>
    public char ColumnTypeChar
    { 
      get
      { 
        switch(mType)
        {
          case DbfColumnType.Number:
            return 'N';
          
          case DbfColumnType.Character:
            return 'C';
          
          case DbfColumnType.Binary:
            return 'B';
            
          case DbfColumnType.Boolean:
            return 'L';
          
          case DbfColumnType.Date:
            return 'D';
          
          case DbfColumnType.Integer:
            return 'I';

          case DbfColumnType.Memo:
            return 'M';
          
        }
        
        throw new Exception("Unrecognized field type!");
        
      }
    }
    
    
    /// <summary>
    /// Field Data Address offset from the start of the record.
    /// </summary>
    public int DataAddress
    { 
      get
      { 
        return mDataAddress;
      }
    }

    /// <summary>
    /// Length of the data in bytes.
    /// </summary>
    public int Length
    { 
      get
      { 
        return mLength;
      }
    }

    /// <summary>
    /// Field decimal count in Binary, indicating where the decimal is.
    /// </summary>
    public int DecimalCount
    { 
      get
      { 
        return mDecimalCount;
      }
      
    }
    
    
    
    
    
    /// <summary>
    /// Returns corresponding dbf field type given a .net Type.
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static DbfColumnType GetDbaseType(Type type)
    { 
      
      if (type == typeof(string))
        return DbfColumnType.Character;
      else if (type == typeof(double) || type == typeof(float))
        return DbfColumnType.Number;
      else if (type == typeof(bool))
        return DbfColumnType.Boolean;
      else if (type == typeof(DateTime))
        return DbfColumnType.Date;
      
      throw new NotSupportedException(String.Format("{0} does not have a corresponding dbase type.", type.Name));
      
    }
    
    public static DbfColumnType GetDbaseType(char c)
    { 
      switch(c.ToString().ToUpper())
      { 
        case "C": return DbfColumnType.Character;
        case "N": return DbfColumnType.Number;
        case "B": return DbfColumnType.Binary;
        case "L": return DbfColumnType.Boolean;
        case "D": return DbfColumnType.Date;
        case "I": return DbfColumnType.Integer;
        case "M": return DbfColumnType.Memo;
      }
      
      throw new NotSupportedException(String.Format("{0} does not have a corresponding dbase type.", c));
      
    }
    
    /// <summary>
    /// Returns shp file Shape Field.
    /// </summary>
    /// <returns></returns>
    public static DbfColumn ShapeField()
    { 
      return new DbfColumn("Geometry", DbfColumnType.Binary);
      
    }
    
    
    /// <summary>
    /// Returns Shp file ID field.
    /// </summary>
    /// <returns></returns>
    public static DbfColumn IdField()
    { 
      return new DbfColumn("Row", DbfColumnType.Integer);
      
    }



    public object Clone()
    {
        return this.MemberwiseClone();
    }
  }
}
View Code

源檔案:DbfDataTruncateException.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;


namespace SocialExplorer.IO.FastDBF
{ 
  
  public class DbfDataTruncateException: Exception
  { 
    
    public DbfDataTruncateException(string smessage): base(smessage)
    { 
    }

    public DbfDataTruncateException(string smessage, Exception innerException)
      : base(smessage, innerException)
    { 
    }

    public DbfDataTruncateException(SerializationInfo info, StreamingContext context)
      : base(info, context)
    {
    }
    
  }
}
View Code

源檔案:DbfFile.cs

///
/// Author: Ahmed Lacevic
/// Date: 12/1/2007
/// Desc: This class represents a DBF file. You can create, open, update and save DBF files using this class and supporting classes.
/// Also, this class supports reading/writing from/to an internet forward only type of stream!
/// 
/// Revision History:
/// -----------------------------------
///   Author:
///   Date:
///   Desc:


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


namespace SocialExplorer.IO.FastDBF
{

    /// <summary>
    /// This class represents a DBF file. You can create new, open, update and save DBF files using this class and supporting classes.
    /// Also, this class supports reading/writing from/to an internet forward only type of stream!
    /// </summary>
    /// <remarks>
    /// TODO: add end of file byte '0x1A' !!!
    /// We don't relly on that byte at all, and everything works with or without that byte, but it should be there by spec.
    /// </remarks>
    public class DbfFile
    {

        /// <summary>
        /// Helps read/write dbf file header information.
        /// </summary>
        protected DbfHeader mHeader;


        /// <summary>
        /// flag that indicates whether the header was written or not...
        /// </summary>
        protected bool mHeaderWritten = false;


        /// <summary>
        /// Streams to read and write to the DBF file.
        /// </summary>
        protected Stream mDbfFile = null;
        protected BinaryReader mDbfFileReader = null;
        protected BinaryWriter mDbfFileWriter = null;

        private Encoding encoding = Encoding.ASCII;

        /// <summary>
        /// File that was opened, if one was opened at all.
        /// </summary>
        protected string mFileName = "";


        /// <summary>
        /// Number of records read using ReadNext() methods only. This applies only when we are using a forward-only stream.
        /// mRecordsReadCount is used to keep track of record index. With a seek enabled stream, 
        /// we can always calculate index using stream position.
        /// </summary>
        protected int mRecordsReadCount = 0;


        /// <summary>
        /// keep these values handy so we don't call functions on every read.
        /// </summary>
        protected bool mIsForwardOnly = false;
        protected bool mIsReadOnly = false;

        [Obsolete]
        public DbfFile()
            : this(Encoding.ASCII)
        {
        }

        public DbfFile(Encoding encoding)
        {
            this.encoding = encoding;
            mHeader = new DbfHeader(encoding);
        }

        /// <summary>
        /// Open a DBF from a FileStream. This can be a file or an internet connection stream. Make sure that it is positioned at start of DBF file.
        /// Reading a DBF over the internet we can not determine size of the file, so we support HasMore(), ReadNext() interface. 
        /// RecordCount information in header can not be trusted always, since some packages store 0 there.
        /// </summary>
        /// <param name="ofs"></param>
        public void Open(Stream ofs)
        {
            if (mDbfFile != null)
                Close();

            mDbfFile = ofs;
            mDbfFileReader = null;
            mDbfFileWriter = null;

            if (mDbfFile.CanRead)
                mDbfFileReader = new BinaryReader(mDbfFile, encoding);

            if (mDbfFile.CanWrite)
                mDbfFileWriter = new BinaryWriter(mDbfFile, encoding);

            //reset position
            mRecordsReadCount = 0;

            //assume header is not written
            mHeaderWritten = false;

            //read the header
            if (ofs.CanRead)
            {
                //try to read the header...
                try
                {
                    mHeader.Read(mDbfFileReader);
                    mHeaderWritten = true;

                }
                catch (EndOfStreamException)
                {
                    //could not read header, file is empty
                    mHeader = new DbfHeader(encoding);
                    mHeaderWritten = false;
                }


            }

            if (mDbfFile != null)
            {
                mIsReadOnly = !mDbfFile.CanWrite;
                mIsForwardOnly = !mDbfFile.CanSeek;
            }


        }



        /// <summary>
        /// Open a DBF file or create a new one.
        /// </summary>
        /// <param name="sPath">Full path to the file.</param>
        /// <param name="mode"></param>
        public void Open(string sPath, FileMode mode, FileAccess access, FileShare share)
        {
            mFileName = sPath;
            Open(File.Open(sPath, mode, access, share));
        }

        /// <summary>
        /// Open a DBF file or create a new one.
        /// </summary>
        /// <param name="sPath">Full path to the file.</param>
        /// <param name="mode"></param>
        public void Open(string sPath, FileMode mode, FileAccess access)
        {
            mFileName = sPath;
            Open(File.Open(sPath, mode, access));
        }

        /// <summary>
        /// Open a DBF file or create a new one.
        /// </summary>
        /// <param name="sPath">Full path to the file.</param>
        /// <param name="mode"></param>
        public void Open(string sPath, FileMode mode)
        {
            mFileName = sPath;
            Open(File.Open(sPath, mode));
        }


        /// <summary>
        /// Creates a new DBF 4 file. Overwrites if file exists! Use Open() function for more options.
        /// </summary>
        /// <param name="sPath"></param>
        public void Create(string sPath)
        {
            Open(sPath, FileMode.Create, FileAccess.ReadWrite);
            mHeaderWritten = false;

        }



        /// <summary>
        /// Update header info, flush buffers and close streams. You should always call this method when you are done with a DBF file.
        /// </summary>
        public void Close()
        {

            //try to update the header if it has changed
            //------------------------------------------
            if (mHeader.IsDirty)
                WriteHeader();



            //Empty header...
            //--------------------------------
            mHeader = new DbfHeader(encoding);
            mHeaderWritten = false;


            //reset current record index
            //--------------------------------
            mRecordsReadCount = 0;


            //Close streams...
            //--------------------------------
            if (mDbfFileWriter != null)
            {
                mDbfFileWriter.Flush();
                mDbfFileWriter.Close();
            }

            if (mDbfFileReader != null)
                mDbfFileReader.Close();

            if (mDbfFile != null)
                mDbfFile.Close();


            //set streams to null
            //--------------------------------
            mDbfFileReader = null;
            mDbfFileWriter = null;
            mDbfFile = null;

            mFileName = "";

        }



        /// <summary>
        /// Returns true if we can not write to the DBF file stream.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return mIsReadOnly;
                /*
                if (mDbfFile != null)
                  return !mDbfFile.CanWrite; 
                return true;
                */

            }

        }


        /// <summary>
        /// Returns true if we can not seek to different locations within the file, such as internet connections.
        /// </summary>
        public bool IsForwardOnly
        {
            get
            {
                return mIsForwardOnly;
                /*
                if(mDbfFile!=null)
                  return !mDbfFile.CanSeek;
        
                return false;
                */
            }
        }


        /// <summary>
        /// Returns the name of the filestream.
        /// </summary>
        public string FileName
        {
            get
            {
                return mFileName;
            }
        }



        /// <summary>
        /// Read next record and fill data into parameter oFillRecord. Returns true if a record was read, otherwise false.
        /// </summary>
        /// <param name="oFillRecord"></param>
        /// <returns></returns>
        public bool ReadNext(DbfRecord oFillRecord)
        {

            //check if we can fill this record with data. it must match record size specified by header and number of columns.
            //we are not checking whether it comes from another DBF file or not, we just need the same structure. Allow flexibility but be safe.
            if (oFillRecord.Header != mHeader && (oFillRecord.Header.ColumnCount != mHeader.ColumnCount || oFillRecord.Header.RecordLength != mHeader.RecordLength))
                throw new Exception("Record parameter does not have the same size and number of columns as the " +
                                    "header specifies, so we are unable to read a record into oFillRecord. " +
                                    "This is a programming error, have you mixed up DBF file objects?");

            //DBF file reader can be null if stream is not readable...
            if (mDbfFileReader == null)
                throw new Exception("Read stream is null, either you have opened a stream that can not be " +
                                    "read from (a write-only stream) or you have not opened a stream at all.");

            //read next record...
            bool bRead = oFillRecord.Read(mDbfFile);

            if (bRead)
            {
                if (mIsForwardOnly)
                {
                    //zero based index! set before incrementing count.
                    oFillRecord.RecordIndex = mRecordsReadCount;
                    mRecordsReadCount++;
                }
                else
                    oFillRecord.RecordIndex = ((int)((mDbfFile.Position - mHeader.HeaderLength) / mHeader.RecordLength)) - 1;

            }

            return bRead;

        }


        /// <summary>
        /// Tries to read a record and returns a new record object or null if nothing was read.
        /// </summary>
        /// <returns></returns>
        public DbfRecord ReadNext()
        {
            //create a new record and fill it.
            DbfRecord orec = new DbfRecord(mHeader);

            return ReadNext(orec) ? orec : null;

        }



        /// <summary>
        /// Reads a record specified by index into oFillRecord object. You can use this method 
        /// to read in and process records without creating and discarding record objects.
        /// Note that you should check that your stream is not forward-only! If you have a forward only stream, use ReadNext() functions.
        /// </summary>
        /// <param name="index">Zero based record index.</param>
        /// <param name="oFillRecord">Record object to fill, must have same size and number of fields as thid DBF file header!</param>
        /// <remarks>
        /// <returns>True if read a record was read, otherwise false. If you read end of file false will be returned and oFillRecord will NOT be modified!</returns>
        /// The parameter record (oFillRecord) must match record size specified by the header and number of columns as well.
        /// It does not have to come from the same header, but it must match the structure. We are not going as far as to check size of each field.
        /// The idea is to be flexible but safe. It's a fine balance, these two are almost always at odds.
        /// </remarks>
        public bool Read(int index, DbfRecord oFillRecord)
        {

            //check if we can fill this record with data. it must match record size specified by header and number of columns.
            //we are not checking whether it comes from another DBF file or not, we just need the same structure. Allow flexibility but be safe.
            if (oFillRecord.Header != mHeader && (oFillRecord.Header.ColumnCount != mHeader.ColumnCount || oFillRecord.Header.RecordLength != mHeader.RecordLength))
                throw new Exception("Record parameter does not have the same size and number of columns as the " +
                                    "header specifies, so we are unable to read a record into oFillRecord. " +
                                    "This is a programming error, have you mixed up DBF file objects?");

            //DBF file reader can be null if stream is not readable...
            if (mDbfFileReader == null)
                throw new Exception("ReadStream is null, either you have opened a stream that can not be " +
                                    "read from (a write-only stream) or you have not opened a stream at all.");


            //move to the specified record, note that an exception will be thrown is stream is not seekable! 
            //This is ok, since we provide a function to check whether the stream is seekable. 
            long nSeekToPosition = mHeader.HeaderLength + (index * mHeader.RecordLength);

            //check whether requested record exists. Subtract 1 from file length (there is a terminating character 1A at the end of the file)
            //so if we hit end of file, there are no more records, so return false;
            if (index < 0 || mDbfFile.Length - 1 <= nSeekToPosition)
                return false;

            //move to record and read
            mDbfFile.Seek(nSeekToPosition, SeekOrigin.Begin);

            //read the record
            bool bRead = oFillRecord.Read(mDbfFile);
            if (bRead)
                oFillRecord.RecordIndex = index;

            return bRead;

        }

        public bool ReadValue(int rowIndex, int columnIndex, out string result)
        {

            result = String.Empty;

            DbfColumn ocol = mHeader[columnIndex];

            //move to the specified record, note that an exception will be thrown is stream is not seekable! 
            //This is ok, since we provide a function to check whether the stream is seekable. 
            long nSeekToPosition = mHeader.HeaderLength + (rowIndex * mHeader.RecordLength) + ocol.DataAddress;

            //check whether requested record exists. Subtract 1 from file length (there is a terminating character 1A at the end of the file)
            //so if we hit end of file, there are no more records, so return false;
            if (rowIndex < 0 || mDbfFile.Length - 1 <= nSeekToPosition)
                return false;

            //move to position and read
            mDbfFile.Seek(nSeekToPosition, SeekOrigin.Begin);

            //read the value
            byte[] data = https://www.cnblogs.com/alannever/p/new byte[ocol.Length];
            mDbfFile.Read(data, 0, ocol.Length);
            result = new string(encoding.GetChars(data, 0, ocol.Length));

            return true;
        }

        /// <summary>
        /// Reads a record specified by index. This method requires the stream to be able to seek to position. 
        /// If you are using a http stream, or a stream that can not stream, use ReadNext() methods to read in all records.
        /// </summary>
        /// <param name="index">Zero based index.</param>
        /// <returns>Null if record can not be read, otherwise returns a new record.</returns>
        public DbfRecord Read(int index)
        {
            //create a new record and fill it.
            DbfRecord orec = new DbfRecord(mHeader);

            return Read(index, orec) ? orec : null;

        }




        /// <summary>
        /// Write a record to file. If RecordIndex is present, record will be updated, otherwise a new record will be written.
        /// Header will be output first if this is the first record being writen to file. 
        /// This method does not require stream seek capability to add a new record.
        /// </summary>
        /// <param name="orec"></param>
        public void Write(DbfRecord orec)
        {

            //if header was never written, write it first, then output the record
            if (!mHeaderWritten)
                WriteHeader();

            //if this is a new record (RecordIndex should be -1 in that case)
            if (orec.RecordIndex < 0)
            {
                if (mDbfFileWriter.BaseStream.CanSeek)
                {
                    //calculate number of records in file. do not rely on header's RecordCount property since client can change that value.
                    //also note that some DBF files do not have ending 0x1A byte, so we subtract 1 and round off 
                    //instead of just cast since cast would just drop decimals.
                    int nNumRecords = (int)Math.Round(((double)(mDbfFile.Length - mHeader.HeaderLength - 1) / mHeader.RecordLength));
                    if (nNumRecords < 0)
                        nNumRecords = 0;

                    orec.RecordIndex = nNumRecords;
                    Update(orec);
                    mHeader.RecordCount++;

                }
                else
                {
                    //we can not position this stream, just write out the new record.
                    orec.Write(mDbfFile);
                    mHeader.RecordCount++;
                }
            }
            else
                Update(orec);

        }

        public void Write(DbfRecord orec, bool bClearRecordAfterWrite)
        {

            Write(orec);

            if (bClearRecordAfterWrite)
                orec.Clear();

        }


        /// <summary>
        /// Update a record. RecordIndex (zero based index) must be more than -1, otherwise an exception is thrown.
        /// You can also use Write method which updates a record if it has RecordIndex or adds a new one if RecordIndex == -1.
        /// RecordIndex is set automatically when you call any Read() methods on this class.
        /// </summary>
        /// <param name="orec"></param>
        public void Update(DbfRecord orec)
        {

            //if header was never written, write it first, then output the record
            if (!mHeaderWritten)
                WriteHeader();


            //Check if record has an index
            if (orec.RecordIndex < 0)
                throw new Exception("RecordIndex is not set, unable to update record. Set RecordIndex or call Write() method to add a new record to file.");


            //Check if this record matches record size specified by header and number of columns. 
            //Client can pass a record from another DBF that is incompatible with this one and that would corrupt the file.
            if (orec.Header != mHeader && (orec.Header.ColumnCount != mHeader.ColumnCount || orec.Header.RecordLength != mHeader.RecordLength))
                throw new Exception("Record parameter does not have the same size and number of columns as the " +
                                    "header specifies. Writing this record would corrupt the DBF file. " +
                                    "This is a programming error, have you mixed up DBF file objects?");

            //DBF file writer can be null if stream is not writable to...
            if (mDbfFileWriter == null)
                throw new Exception("Write stream is null. Either you have opened a stream that can not be " +
                                    "writen to (a read-only stream) or you have not opened a stream at all.");


            //move to the specified record, note that an exception will be thrown if stream is not seekable! 
            //This is ok, since we provide a function to check whether the stream is seekable. 
            long nSeekToPosition = (long)mHeader.HeaderLength + (long)((long)orec.RecordIndex * (long)mHeader.RecordLength);

            //check whether we can seek to this position. Subtract 1 from file length (there is a terminating character 1A at the end of the file)
            //so if we hit end of file, there are no more records, so return false;
            if (mDbfFile.Length < nSeekToPosition)
                throw new Exception("Invalid record position. Unable to save record.");

            //move to record start
            mDbfFile.Seek(nSeekToPosition, SeekOrigin.Begin);

            //write
            orec.Write(mDbfFile);


        }



        /// <summary>
        /// Save header to file. Normally, you do not have to call this method, header is saved 
        /// automatically and updated when you close the file (if it changed).
        /// </summary>
        public bool WriteHeader()
        {

            //update header if possible
            //--------------------------------
            if (mDbfFileWriter != null)
            {
                if (mDbfFileWriter.BaseStream.CanSeek)
                {
                    mDbfFileWriter.Seek(0, SeekOrigin.Begin);
                    mHeader.Write(mDbfFileWriter);
                    mHeaderWritten = true;
                    return true;
                }
                else
                {
                    //if stream can not seek, then just write it out and that's it.
                    if (!mHeaderWritten)
                        mHeader.Write(mDbfFileWriter);

                    mHeaderWritten = true;

                }
            }

            return false;

        }



        /// <summary>
        /// Access DBF header with information on columns. Use this object for faster access to header. 
        /// Remove one layer of function calls by saving header reference and using it directly to access columns.
        /// </summary>
        public DbfHeader Header
        {
            get
            {
                return mHeader;
            }
        }



    }
}
View Code

源檔案:DbfHeader.cs

///
/// Author: Ahmed Lacevic
/// Date: 12/1/2007
/// Desc: 
/// 
/// Revision History:
/// -----------------------------------
///   Author:
///   Date:
///   Desc:


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



namespace SocialExplorer.IO.FastDBF
{

    /// <summary>
    /// This class represents a DBF IV file header.
    /// </summary>
    /// 
    /// <remarks>
    /// DBF files are really wasteful on space but this legacy format lives on because it's really really simple. 
    /// It lacks much in features though.
    /// 
    /// 
    /// Thanks to Erik Bachmann for providing the DBF file structure information!!
    /// http://www.clicketyclick.dk/databases/xbase/format/dbf.html
    /// 
    ///           _______________________  _______
    /// 00h /   0| Version number      *1|  ^
    ///          |-----------------------|  |
    /// 01h /   1| Date of last update   |  |
    /// 02h /   2|      YYMMDD        *21|  |
    /// 03h /   3|                    *14|  |
    ///          |-----------------------|  |
    /// 04h /   4| Number of records     | Record
    /// 05h /   5| in data file          | header
    /// 06h /   6| ( 32 bits )        *14|  |
    /// 07h /   7|                       |  |
    ///          |-----------------------|  |
    /// 08h /   8| Length of header   *14|  |
    /// 09h /   9| structure ( 16 bits ) |  |
    ///          |-----------------------|  |
    /// 0Ah /  10| Length of each record |  |
    /// 0Bh /  11| ( 16 bits )     *2 *14|  |
    ///          |-----------------------|  |
    /// 0Ch /  12| ( Reserved )        *3|  |
    /// 0Dh /  13|                       |  |
    ///          |-----------------------|  |
    /// 0Eh /  14| Incomplete transac.*12|  |
    ///          |-----------------------|  |
    /// 0Fh /  15| Encryption flag    *13|  |
    ///          |-----------------------|  |
    /// 10h /  16| Free record thread    |  |
    /// 11h /  17| (reserved for LAN     |  |
    /// 12h /  18|  only )               |  |
    /// 13h /  19|                       |  |
    ///          |-----------------------|  |
    /// 14h /  20| ( Reserved for        |  |            _        |=======================| ______
    ///          |   multi-user dBASE )  |  |           / 00h /  0| Field name in ASCII   |  ^
    ///          : ( dBASE III+ - )      :  |          /          : (terminated by 00h)   :  |
    ///          :                       :  |         |           |                       |  |
    /// 1Bh /  27|                       |  |         |   0Ah / 10|                       |  |
    ///          |-----------------------|  |         |           |-----------------------| For
    /// 1Ch /  28| MDX flag (dBASE IV)*14|  |         |   0Bh / 11| Field type (ASCII) *20| each
    ///          |-----------------------|  |         |           |-----------------------| field
    /// 1Dh /  29| Language driver     *5|  |        /    0Ch / 12| Field data address    |  |
    ///          |-----------------------|  |       /             |                     *6|  |
    /// 1Eh /  30| ( Reserved )          |  |      /              | (in memory !!!)       |  |
    /// 1Fh /  31|                     *3|  |     /       0Fh / 15| (dBASE III+)          |  |
    ///          |=======================|__|____/                |-----------------------|  |  -
    /// 20h /  32|                       |  |  ^          10h / 16| Field length       *22|  |   |
    ///          |- - - - - - - - - - - -|  |  |                  |-----------------------|  |   | *7
    ///          |                    *19|  |  |          11h / 17| Decimal count      *23|  |   |
    ///          |- - - - - - - - - - - -|  |  Field              |-----------------------|  |  -
    ///          |                       |  | Descriptor  12h / 18| ( Reserved for        |  |
    ///          :. . . . . . . . . . . .:  |  |array     13h / 19|   multi-user dBASE)*18|  |
    ///          :                       :  |  |                  |-----------------------|  |
    ///       n  |                       |__|__v_         14h / 20| Work area ID       *16|  |
    ///          |-----------------------|  |    \                |-----------------------|  |
    ///       n+1| Terminator (0Dh)      |  |     \       15h / 21| ( Reserved for        |  |
    ///          |=======================|  |      \      16h / 22|   multi-user dBASE )  |  |
    ///       m  | Database Container    |  |       \             |-----------------------|  |
    ///          :                    *15:  |        \    17h / 23| Flag for SET FIELDS   |  |
    ///          :                       :  |         |           |-----------------------|  |
    ///     / m+263                      |  |         |   18h / 24| ( Reserved )          |  |
    ///          |=======================|__v_ ___    |           :                       :  |
    ///          :                       :    ^       |           :                       :  |
    ///          :                       :    |       |           :                       :  |
    ///          :                       :    |       |   1Eh / 30|                       |  |
    ///          | Record structure      |    |       |           |-----------------------|  |
    ///          |                       |    |        \  1Fh / 31| Index field flag    *8|  |
    ///          |                       |    |         \_        |=======================| _v_____
    ///          |                       | Records
    ///          |-----------------------|    |
    ///          |                       |    |          _        |=======================| _______
    ///          |                       |    |         / 00h /  0| Record deleted flag *9|  ^
    ///          |                       |    |        /          |-----------------------|  |
    ///          |                       |    |       /           | Data               *10|  One
    ///          |                       |    |      /            : (ASCII)            *17: record
    ///          |                       |____|_____/             |                       |  |
    ///          :                       :    |                   |                       | _v_____
    ///          :                       :____|_____              |=======================|
    ///          :                       :    |
    ///          |                       |    |
    ///          |                       |    |
    ///          |                       |    |
    ///          |                       |    |
    ///          |                       |    |
    ///          |=======================|    |
    ///          |__End_of_File__________| ___v____  End of file ( 1Ah )  *11
    /// 
    /// </remarks>
    public class DbfHeader : ICloneable
    {

        /// <summary>
        /// Header file descriptor size is 33 bytes (32 bytes + 1 terminator byte), followed by column metadata which is 32 bytes each.
        /// </summary>
        public const int FileDescriptorSize = 33;


        /// <summary>
        /// Field or DBF Column descriptor is 32 bytes long.
        /// </summary>
        public const int ColumnDescriptorSize = 32;


        //type of the file, must be 03h
        private const int mFileType = 0x03;

        //Date the file was last updated.
        private DateTime mUpdateDate;

        //Number of records in the datafile, 32bit little-endian, unsigned 
        private uint mNumRecords = 0;

        //Length of the header structure
        private ushort mHeaderLength = FileDescriptorSize;  //empty header is 33 bytes long. Each column adds 32 bytes.

        //Length of the records, ushort - unsigned 16 bit integer
        private int mRecordLength = 1;  //start with 1 because the first byte is a delete flag

        //DBF fields/columns
        internal List<DbfColumn> mFields = new List<DbfColumn>();


        //indicates whether header columns can be modified!
        bool mLocked = false;

        //keeps column name index for the header, must clear when header columns change.
        private Dictionary<string, int> mColumnNameIndex = null;

        /// <summary>
        /// When object is modified dirty flag is set.
        /// </summary>
        bool mIsDirty = false;


        /// <summary>
        /// mEmptyRecord is an array used to clear record data in CDbf4Record.
        /// This is shared by all record objects, used to speed up clearing fields or entire record.
        /// <seealso cref="EmptyDataRecord"/>
        /// </summary>
        private byte[] mEmptyRecord = null;


        public readonly Encoding encoding = Encoding.ASCII;


        [Obsolete]
        public DbfHeader()
        {
        }

        public DbfHeader(Encoding encoding)
        {
            this.encoding = encoding;
        }


        /// <summary>
        /// Specify initial column capacity.
        /// </summary>
        /// <param name="nInitialFields"></param>
        public DbfHeader(int nFieldCapacity)
        {
            mFields = new List<DbfColumn>(nFieldCapacity);

        }


        /// <summary>
        /// Gets header length.
        /// </summary>
        public ushort HeaderLength
        {
            get
            {
                return mHeaderLength;
            }
        }


        /// <summary>
        /// Add a new column to the DBF header.
        /// </summary>
        /// <param name="oNewCol"></param>
        public void AddColumn(DbfColumn oNewCol)
        {

            //throw exception if the header is locked
            if (mLocked)
                throw new InvalidOperationException("This header is locked and can not be modified. Modifying the header would result in a corrupt DBF file. You can unlock the header by calling UnLock() method.");

            //since we are breaking the spec rules about max number of fields, we should at least 
            //check that the record length stays within a number that can be recorded in the header!
            //we have 2 unsigned bytes for record length for a maximum of 65535.
            if (mRecordLength + oNewCol.Length > 65535)
                throw new ArgumentOutOfRangeException("oNewCol", "Unable to add new column. Adding this column puts the record length over the maximum (which is 65535 bytes).");


            //add the column
            mFields.Add(oNewCol);

            //update offset bits, record and header lengths
            oNewCol.mDataAddress = mRecordLength;
            mRecordLength += oNewCol.Length;
            mHeaderLength += ColumnDescriptorSize;

            //clear empty record
            mEmptyRecord = null;

            //set dirty bit
            mIsDirty = true;
            mColumnNameIndex = null;

        }


        /// <summary>
        /// Create and add a new column with specified name and type.
        /// </summary>
        /// <param name="sName"></param>
        /// <param name="type"></param>
        public void AddColumn(string sName, DbfColumn.DbfColumnType type)
        {
            AddColumn(new DbfColumn(sName, type));
        }


        /// <summary>
        /// Create and add a new column with specified name, type, length, and decimal precision.
        /// </summary>
        /// <param name="sName">Field name. Uniqueness is not enforced.</param>
        /// <param name="type"></param>
        /// <param name="nLength">Length of the field including decimal point and decimal numbers</param>
        /// <param name="nDecimals">Number of decimal places to keep.</param>
        public void AddColumn(string sName, DbfColumn.DbfColumnType type, int nLength, int nDecimals)
        {
            AddColumn(new DbfColumn(sName, type, nLength, nDecimals));
        }


        /// <summary>
        /// Remove column from header definition.
        /// </summary>
        /// <param name="nIndex"></param>
        public void RemoveColumn(int nIndex)
        {
            //throw exception if the header is locked
            if (mLocked)
                throw new InvalidOperationException("This header is locked and can not be modified. Modifying the header would result in a corrupt DBF file. You can unlock the header by calling UnLock() method.");


            DbfColumn oColRemove = mFields[nIndex];
            mFields.RemoveAt(nIndex);


            oColRemove.mDataAddress = 0;
            mRecordLength -= oColRemove.Length;
            mHeaderLength -= ColumnDescriptorSize;

            //if you remove a column offset shift for each of the columns 
            //following the one removed, we need to update those offsets.
            int nRemovedColLen = oColRemove.Length;
            for (int i = nIndex; i < mFields.Count; i++)
                mFields[i].mDataAddress -= nRemovedColLen;

            //clear the empty record
            mEmptyRecord = null;

            //set dirty bit
            mIsDirty = true;
            mColumnNameIndex = null;

        }


        /// <summary>
        /// Look up a column index by name. Note that this is case sensitive, internally it does a lookup using a dictionary.
        /// </summary>
        /// <param name="sName"></param>
        public DbfColumn this[string sName]
        {
            get
            {
                int colIndex = FindColumn(sName);
                if (colIndex > -1)
                    return mFields[colIndex];

                return null;

            }
        }


        /// <summary>
        /// Returns column at specified index. Index is 0 based.
        /// </summary>
        /// <param name="nIndex">Zero based index.</param>
        /// <returns></returns>
        public DbfColumn this[int nIndex]
        {
            get
            {
                return mFields[nIndex];
            }
        }


        /// <summary>
        /// Finds a column index by using a fast dictionary lookup-- creates column dictionary on first use. Returns -1 if not found. Note this is case sensitive!
        /// </summary>
        /// <param name="sName">Column name</param>
        /// <returns>column index (0 based) or -1 if not found.</returns>
        public int FindColumn(string sName)
        {

            if (mColumnNameIndex == null)
            {
                mColumnNameIndex = new Dictionary<string, int>(mFields.Count);

                //create a new index
                for (int i = 0; i < mFields.Count; i++)
                {
                    mColumnNameIndex.Add(mFields[i].Name, i);
                }
            }

            int columnIndex;
            if (mColumnNameIndex.TryGetValue(sName, out columnIndex))
                return columnIndex;

            return -1;

        }


        /// <summary>
        /// Returns an empty data record. This is used to clear columns 
        /// </summary>
        /// <remarks>
        /// The reason we put this in the header class is because it allows us to use the CDbf4Record class in two ways.
        /// 1. we can create one instance of the record and reuse it to write many records quickly clearing the data array by bitblting to it.
        /// 2. we can create many instances of the record (a collection of records) and have only one copy of this empty dataset for all of them.
        ///    If we had put it in the Record class then we would be taking up twice as much space unnecessarily. The empty record also fits the model
        ///    and everything is neatly encapsulated and safe.
        /// 
        /// </remarks>
        protected internal byte[] EmptyDataRecord
        {
            get { return mEmptyRecord ?? (mEmptyRecord = encoding.GetBytes("".PadLeft(mRecordLength, ' ').ToCharArray())); }
        }


        /// <summary>
        /// Returns Number of columns in this dbf header.
        /// </summary>
        public int ColumnCount
        {
            get { return mFields.Count; }

        }


        /// <summary>
        /// Size of one record in bytes. All fields + 1 byte delete flag.
        /// </summary>
        public int RecordLength
        {
            get
            {
                return mRecordLength;
            }
        }


        /// <summary>
        /// Get/Set number of records in the DBF.
        /// </summary>
        /// <remarks>
        /// The reason we allow client to set RecordCount is beause in certain streams 
        /// like internet streams we can not update record count as we write out records, we have to set it in advance,
        /// so client has to be able to modify this property.
        /// </remarks>
        public uint RecordCount
        {
            get
            {
                return mNumRecords;
            }

            set
            {
                mNumRecords = value;

                //set the dirty bit
                mIsDirty = true;

            }
        }


        /// <summary>
        /// Get/set whether this header is read only or can be modified. When you create a CDbfRecord 
        /// object and pass a header to it, CDbfRecord locks the header so that it can not be modified any longer.
        /// in order to preserve DBF integrity.
        /// </summary>
        internal bool Locked
        {
            get
            {
                return mLocked;
            }

            set
            {
                mLocked = value;
            }

        }


        /// <summary>
        /// Use this method with caution. Headers are locked for a reason, to prevent DBF from becoming corrupt.
        /// </summary>
        public void Unlock()
        {
            mLocked = false;
        }


        /// <summary>
        /// Returns true when this object is modified after read or write.
        /// </summary>
        public bool IsDirty
        {
            get
            {
                return mIsDirty;
            }

            set
            {
                mIsDirty = value;
            }
        }


        /// <summary>
        /// Encoding must be ASCII for this binary writer.
        /// </summary>
        /// <param name="writer"></param>
        /// <remarks>
        /// See class remarks for DBF file structure.
        /// </remarks>
        public void Write(BinaryWriter writer)
        {

            //write the header
            // write the output file type.
            writer.Write((byte)mFileType);

            //Update date format is YYMMDD, which is different from the column Date type (YYYYDDMM)
            writer.Write((byte)(mUpdateDate.Year - 1900));
            writer.Write((byte)mUpdateDate.Month);
            writer.Write((byte)mUpdateDate.Day);

            // write the number of records in the datafile. (32 bit number, little-endian unsigned)
            writer.Write(mNumRecords);

            // write the length of the header structure.
            writer.Write(mHeaderLength);

            // write the length of a record
            writer.Write((ushort)mRecordLength);

            // write the reserved bytes in the header
            for (int i = 0; i < 20; i++)
                writer.Write((byte)0);

            // write all of the header records
            byte[] byteReserved = new byte[14];  //these are initialized to 0 by default.
            foreach (DbfColumn field in mFields)
            {
                //char[] cname = field.Name.PadRight(11, (char)0).ToCharArray();
                byte[] bName = encoding.GetBytes(field.Name);
                byte[] cname = new byte[11];
                Array.ConstrainedCopy(bName, 0, cname, 0, bName.Length > 11 ? 11 : bName.Length);

                writer.Write(cname);

                // write the field type
                writer.Write((char)field.ColumnTypeChar);

                // write the field data address, offset from the start of the record.
                writer.Write(field.DataAddress);


                // write the length of the field.
                // if char field is longer than 255 bytes, then we use the decimal field as part of the field length.
                if (field.ColumnType == DbfColumn.DbfColumnType.Character && field.Length > 255)
                {
                    //treat decimal count as high byte of field length, this extends char field max to 65535
                    writer.Write((ushort)field.Length);

                }
                else
                {
                    // write the length of the field.
                    writer.Write((byte)field.Length);

                    // write the decimal count.
                    writer.Write((byte)field.DecimalCount);
                }

                // write the reserved bytes.
                writer.Write(byteReserved);

            }

            // write the end of the field definitions marker
            writer.Write((byte)0x0D);
            writer.Flush();

            //clear dirty bit
            mIsDirty = false;


            //lock the header so it can not be modified any longer, 
            //we could actually postpond this until first record is written!
            mLocked = true;


        }


        /// <summary>
        /// Read header data, make sure the stream is positioned at the start of the file to read the header otherwise you will get an exception.
        /// When this function is done the position will be the first record.
        /// </summary>
        /// <param name="reader"></param>
        public void Read(BinaryReader reader)
        {

            // type of reader.
            int nFileType = reader.ReadByte();

            if (nFileType != 0x03)
                throw new NotSupportedException("Unsupported DBF reader Type " + nFileType);

            // parse the update date information.
            int year = (int)reader.ReadByte();
            int month = (int)reader.ReadByte();
            int day = (int)reader.ReadByte();
            mUpdateDate = new DateTime(year + 1900, month, day);

            // read the number of records.
            mNumRecords = reader.ReadUInt32();

            // read the length of the header structure.
            mHeaderLength = reader.ReadUInt16();

            // read the length of a record
            mRecordLength = reader.ReadInt16();

            // skip the reserved bytes in the header.
            reader.ReadBytes(20);

            // calculate the number of Fields in the header
            int nNumFields = (mHeaderLength - FileDescriptorSize) / ColumnDescriptorSize;

            //offset from start of record, start at 1 because that's the delete flag.
            int nDataOffset = 1;

            // read all of the header records
            mFields = new List<DbfColumn>(nNumFields);
            for (int i = 0; i < nNumFields; i++)
            {

                // read the field name                
                char[] buffer = new char[11];
                buffer = reader.ReadChars(11);
                string sFieldName = new string(buffer);
                int nullPoint = sFieldName.IndexOf((char)0);
                if (nullPoint != -1)
                    sFieldName = sFieldName.Substring(0, nullPoint);


                //read the field type
                char cDbaseType = (char)reader.ReadByte();

                // read the field data address, offset from the start of the record.
                int nFieldDataAddress = reader.ReadInt32();


                //read the field length in bytes
                //if field type is char, then read FieldLength and Decimal count as one number to allow char fields to be
                //longer than 256 bytes (ASCII char). This is the way Clipper and FoxPro do it, and there is really no downside
                //since for char fields decimal count should be zero for other versions that do not support this extended functionality.
                //-----------------------------------------------------------------------------------------------------------------------
                int nFieldLength = 0;
                int nDecimals = 0;
                if (cDbaseType == 'C' || cDbaseType == 'c')
                {
                    //treat decimal count as high byte
                    nFieldLength = (int)reader.ReadUInt16();
                }
                else
                {
                    //read field length as an unsigned byte.
                    nFieldLength = (int)reader.ReadByte();

                    //read decimal count as one byte
                    nDecimals = (int)reader.ReadByte();

                }


                //read the reserved bytes.
                reader.ReadBytes(14);

                //Create and add field to collection
                mFields.Add(new DbfColumn(sFieldName, DbfColumn.GetDbaseType(cDbaseType), nFieldLength, nDecimals, nDataOffset));

                // add up address information, you can not trust the address recorded in the DBF file...
                nDataOffset += nFieldLength;

            }

            // Last byte is a marker for the end of the field definitions.
            reader.ReadBytes(1);


            //read any extra header bytes...move to first record
            //equivalent to reader.BaseStream.Seek(mHeaderLength, SeekOrigin.Begin) except that we are not using the seek function since
            //we need to support streams that can not seek like web connections.
            int nExtraReadBytes = mHeaderLength - (FileDescriptorSize + (ColumnDescriptorSize * mFields.Count));
            if (nExtraReadBytes > 0)
                reader.ReadBytes(nExtraReadBytes);



            //if the stream is not forward-only, calculate number of records using file size, 
            //sometimes the header does not contain the correct record count
            //if we are reading the file from the web, we have to use ReadNext() functions anyway so
            //Number of records is not so important and we can trust the DBF to have it stored correctly.
            if (reader.BaseStream.CanSeek && mNumRecords == 0)
            {
                //notice here that we subtract file end byte which is supposed to be 0x1A,
                //but some DBF files are incorrectly written without this byte, so we round off to nearest integer.
                //that gives a correct result with or without ending byte.
                if (mRecordLength > 0)
                    mNumRecords = (uint)Math.Round(((double)(reader.BaseStream.Length - mHeaderLength - 1) / mRecordLength));

            }


            //lock header since it was read from a file. we don't want it modified because that would corrupt the file.
            //user can override this lock if really necessary by calling UnLock() method.
            mLocked = true;

            //clear dirty bit
            mIsDirty = false;

        }



        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }
}
View Code

源檔案:DbfRecord.cs

///
/// Author: Ahmed Lacevic
/// Date: 12/1/2007
/// Desc: 
/// 
/// Revision History:
/// -----------------------------------
///   Author:
///   Date:
///   Desc:


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


namespace SocialExplorer.IO.FastDBF
{

    /// <summary>
    /// Use this class to create a record and write it to a dbf file. You can use one record object to write all records!!
    /// It was designed for this kind of use. You can do this by clearing the record of all data 
    /// (call Clear() method) or setting values to all fields again, then write to dbf file. 
    /// This eliminates creating and destroying objects and optimizes memory use.
    /// 
    /// Once you create a record the header can no longer be modified, since modifying the header would make a corrupt DBF file.
    /// </summary>
    public class DbfRecord
    {

        /// <summary>
        /// Header provides information on all field types, sizes, precision and other useful information about the DBF.
        /// </summary>
        private DbfHeader mHeader = null;

        /// <summary>
        /// Dbf data are a mix of ASCII characters and binary, which neatly fit in a byte array.
        /// BinaryWriter would esentially perform the same conversion using the same Encoding class.
        /// </summary>
        private byte[] mData = https://www.cnblogs.com/alannever/p/null;

        /// <summary>
        /// Zero based record index. -1 when not set, new records for example.
        /// </summary>
        private int mRecordIndex = -1;

        /// <summary>
        /// Empty Record array reference used to clear fields quickly (or entire record).
        /// </summary>
        private readonly byte[] mEmptyRecord = null;


        /// <summary>
        /// Specifies whether we allow strings to be truncated. If false and string is longer than we can fit in the field, an exception is thrown.
        /// </summary>
        private bool mAllowStringTruncate = true;

        /// <summary>
        /// Specifies whether we allow the decimal portion of numbers to be truncated. 
        /// If false and decimal digits overflow the field, an exception is thrown.
        /// </summary>
        private bool mAllowDecimalTruncate = false;

        /// <summary>
        /// Specifies whether we allow the integer portion of numbers to be truncated.
        /// If false and integer digits overflow the field, an exception is thrown.
        /// </summary>
        private bool mAllowIntegerTruncate = false;


        //array used to clear decimals, we can clear up to 40 decimals which is much more than is allowed under DBF spec anyway.
        //Note: 48 is ASCII code for 0.
        private static readonly byte[] mDecimalClear = new byte[] {48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
                                                               48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
                                                               48,48,48,48,48,48,48,48,48,48,48,48,48,48,48};


        //Warning: do not make this one static because that would not be thread safe!! The reason I have 
        //placed this here is to skip small memory allocation/deallocation which fragments memory in .net.
        private int[] mTempIntVal = { 0 };


        //Ascii Encoder
        private readonly Encoding encoding = Encoding.ASCII;

        /// <summary>
        /// Column Name to Column Index map
        /// </summary>
        private readonly Dictionary<string, int> mColNameToConIdx = new Dictionary<string, int>(StringComparer.InvariantCulture);



        /// <summary>
        /// 
        /// </summary>
        /// <param name="oHeader">Dbf Header will be locked once a record is created 
        /// since the record size is fixed and if the header was modified it would corrupt the DBF file.</param>
        public DbfRecord(DbfHeader oHeader)
        {
            mHeader = oHeader;
            mHeader.Locked = true;

            //create a buffer to hold all record data. We will reuse this buffer to write all data to the file.
            mData = https://www.cnblogs.com/alannever/p/new byte[mHeader.RecordLength];
            mEmptyRecord = mHeader.EmptyDataRecord;
            encoding = oHeader.encoding;

            for (int i = 0; i < oHeader.mFields.Count; i++)
                mColNameToConIdx[oHeader.mFields[i].Name] = i;
        }


        /// <summary>
        /// Set string data to a column, if the string is longer than specified column length it will be truncated!
        /// If dbf column type is not a string, input will be treated as dbf column 
        /// type and if longer than length an exception will be thrown.
        /// </summary>
        /// <param name="nColIndex"></param>
        /// <returns></returns>
        public string this[int nColIndex]
        {

            set
            {

                DbfColumn ocol = mHeader[nColIndex];
                DbfColumn.DbfColumnType ocolType = ocol.ColumnType;


                //
                //if an empty value is passed, we just clear the data, and leave it blank.
                //note: test have shown that testing for null and checking length is faster than comparing to "" empty str :)
                //------------------------------------------------------------------------------------------------------------
                if (string.IsNullOrEmpty(value))
                {
                    //this is like NULL data, set it to empty. i looked at SAS DBF output when a null value exists 
                    //and empty data are output. we get the same result, so this looks good.
                    Buffer.BlockCopy(mEmptyRecord, ocol.DataAddress, mData, ocol.DataAddress, ocol.Length);

                }
                else
                {

                    //set values according to data type:
                    //-------------------------------------------------------------
                    if (ocolType == DbfColumn.DbfColumnType.Character)
                    {
                        if (!mAllowStringTruncate && value.Length > ocol.Length)
                            throw new DbfDataTruncateException("Value not set. String truncation would occur and AllowStringTruncate flag is set to false. To supress this exception change AllowStringTruncate to true.");

                        //BlockCopy copies bytes.  First clear the previous value, then set the new one.
                        Buffer.BlockCopy(mEmptyRecord, ocol.DataAddress, mData, ocol.DataAddress, ocol.Length);
                        encoding.GetBytes(value, 0, value.Length > ocol.Length ? ocol.Length : value.Length, mData, ocol.DataAddress);

                    }
                    else if (ocolType == DbfColumn.DbfColumnType.Number)
                    {

                        if (ocol.DecimalCount == 0)
                        {

                            //integers
                            //----------------------------------

                            //throw an exception if integer overflow would occur
                            if (!mAllowIntegerTruncate && value.Length > ocol.Length)
                                throw new DbfDataTruncateException("Value not set. Integer does not fit and would be truncated. AllowIntegerTruncate is set to false. To supress this exception set AllowIntegerTruncate to true, although that is not recomended.");


                            //clear all numbers, set to [space].
                            //-----------------------------------------------------
                            Buffer.BlockCopy(mEmptyRecord, 0, mData, ocol.DataAddress, ocol.Length);


                            //set integer part, CAREFUL not to overflow buffer! (truncate instead)
                            //-----------------------------------------------------------------------
                            int nNumLen = value.Length > ocol.Length ? ocol.Length : value.Length;
                            encoding.GetBytes(value, 0, nNumLen, mData, (ocol.DataAddress + ocol.Length - nNumLen));

                        }
                        else
                        {

                            ///TODO: we can improve perfomance here by not using temp char arrays cDec and cNum,
                            ///simply direcly copy from source string using encoding!


                            //break value down into integer and decimal portions
                            //--------------------------------------------------------------------------
                            int nidxDecimal = value.IndexOf('.'); //index where the decimal point occurs
                            char[] cDec = null; //decimal portion of the number
                            char[] cNum = null; //integer portion

                            if (nidxDecimal > -1)
                            {
                                cDec = value.Substring(nidxDecimal + 1).Trim().ToCharArray();
                                cNum = value.Substring(0, nidxDecimal).ToCharArray();

                                //throw an exception if decimal overflow would occur
                                if (!mAllowDecimalTruncate && cDec.Length > ocol.DecimalCount)
                                    throw new DbfDataTruncateException("Value not set. Decimal does not fit and would be truncated. AllowDecimalTruncate is set to false. To supress this exception set AllowDecimalTruncate to true.");

                            }
                            else
                                cNum = value.ToCharArray();


                            //throw an exception if integer overflow would occur
                            if (!mAllowIntegerTruncate && cNum.Length > ocol.Length - ocol.DecimalCount - 1)
                                throw new DbfDataTruncateException("Value not set. Integer does not fit and would be truncated. AllowIntegerTruncate is set to false. To supress this exception set AllowIntegerTruncate to true, although that is not recomended.");



                            //clear all decimals, set to 0.
                            //-----------------------------------------------------
                            Buffer.BlockCopy(mDecimalClear, 0, mData, (ocol.DataAddress + ocol.Length - ocol.DecimalCount), ocol.DecimalCount);

                            //clear all numbers, set to [space].
                            Buffer.BlockCopy(mEmptyRecord, 0, mData, ocol.DataAddress, (ocol.Length - ocol.DecimalCount));



                            //set decimal numbers, CAREFUL not to overflow buffer! (truncate instead)
                            //-----------------------------------------------------------------------
                            if (nidxDecimal > -1)
                            {
                                int nLen = cDec.Length > ocol.DecimalCount ? ocol.DecimalCount : cDec.Length;
                                encoding.GetBytes(cDec, 0, nLen, mData, (ocol.DataAddress + ocol.Length - ocol.DecimalCount));
                            }

                            //set integer part, CAREFUL not to overflow buffer! (truncate instead)
                            //-----------------------------------------------------------------------
                            int nNumLen = cNum.Length > ocol.Length - ocol.DecimalCount - 1 ? (ocol.Length - ocol.DecimalCount - 1) : cNum.Length;
                            encoding.GetBytes(cNum, 0, nNumLen, mData, ocol.DataAddress + ocol.Length - ocol.DecimalCount - nNumLen - 1);


                            //set decimal point
                            //-----------------------------------------------------------------------
                            mData[ocol.DataAddress + ocol.Length - ocol.DecimalCount - 1] = (byte)'.';


                        }


                    }
                    else if (ocolType == DbfColumn.DbfColumnType.Integer)
                    {
                        //note this is a binary Integer type!
                        //----------------------------------------------

                        ///TODO: maybe there is a better way to copy 4 bytes from int to byte array. Some memory function or something.
                        mTempIntVal[0] = Convert.ToInt32(value);
                        Buffer.BlockCopy(mTempIntVal, 0, mData, ocol.DataAddress, 4);

                    }
                    else if (ocolType == DbfColumn.DbfColumnType.Memo)
                    {
                        //copy 10 digits...
                        ///TODO: implement MEMO

                        throw new NotImplementedException("Memo data type functionality not implemented yet!");

                    }
                    else if (ocolType == DbfColumn.DbfColumnType.Boolean)
                    {
                        if (String.Compare(value, "true", true) == 0 || String.Compare(value, "1", true) == 0 ||
                            String.Compare(value, "T", true) == 0 || String.Compare(value, "yes", true) == 0 ||
                            String.Compare(value, "Y", true) == 0)
                            mData[ocol.DataAddress] = (byte)'T';
                        else if (value =https://www.cnblogs.com/alannever/p/= " " || value =https://www.cnblogs.com/alannever/p/= "?")
                            mData[ocol.DataAddress] = (byte)'?';
                        else
                            mData[ocol.DataAddress] = (byte)'F';

                    }
                    else if (ocolType == DbfColumn.DbfColumnType.Date)
                    {
                        //try to parse out date value using Date.Parse() function, then set the value
                        DateTime dateval;
                        if (DateTime.TryParse(value, out dateval))
                        {
                            SetDateValue(nColIndex, dateval);
                        }
                        else
                            throw new InvalidOperationException("Date could not be parsed from source string! Please parse the Date and set the value (you can try using DateTime.Parse() or DateTime.TryParse() functions).");

                    }
                    else if (ocolType == DbfColumn.DbfColumnType.Binary)
                        throw new InvalidOperationException("Can not use string source to set binary data. Use SetBinaryValue() and GetBinaryValue() functions instead.");

                    else
                        throw new InvalidDataException("Unrecognized data type: " + ocolType.ToString());

                }

            }

            get
            {
                DbfColumn ocol = mHeader[nColIndex];
                return new string(encoding.GetChars(mData, ocol.DataAddress, ocol.Length));

            }
        }

        /// <summary>
        /// Set string data to a column, if the string is longer than specified column length it will be truncated!
        /// If dbf column type is not a string, input will be treated as dbf column 
        /// type and if longer than length an exception will be thrown.
        /// </summary>
        /// <param name="nColName"></param>
        /// <returns></returns>
        public string this[string nColName]
        {
            get
            {
                if (mColNameToConIdx.ContainsKey(nColName))
                    return this[mColNameToConIdx[nColName]];
                throw new InvalidOperationException(string.Format("There's no column with name '{0}'", nColName));
            }
            set
            {
                if (mColNameToConIdx.ContainsKey(nColName))
                    this[mColNameToConIdx[nColName]] = value;
                else
                    throw new InvalidOperationException(string.Format("There's no column with name '{0}'", nColName));
            }
        }

        /// <summary>
        /// Get date value.
        /// </summary>
        /// <param name="nColIndex"></param>
        /// <returns></returns>
        public DateTime GetDateValue(int nColIndex)
        {
            DbfColumn ocol = mHeader[nColIndex];

            if (ocol.ColumnType == DbfColumn.DbfColumnType.Date)
            {
                string sDateVal = encoding.GetString(mData, ocol.DataAddress, ocol.Length);
                return DateTime.ParseExact(sDateVal, "yyyyMMdd", CultureInfo.InvariantCulture);

            }
            else
                throw new Exception("Invalid data type. Column '" + ocol.Name + "' is not a date column.");

        }


        /// <summary>
        /// Get date value.
        /// </summary>
        /// <param name="nColIndex"></param>
        /// <returns></returns>
        public void SetDateValue(int nColIndex, DateTime value)
        {

            DbfColumn ocol = mHeader[nColIndex];
            DbfColumn.DbfColumnType ocolType = ocol.ColumnType;


            if (ocolType == DbfColumn.DbfColumnType.Date)
            {

                //Format date and set value, date format is like this: yyyyMMdd
                //-------------------------------------------------------------
                encoding.GetBytes(value.ToString("yyyyMMdd"), 0, ocol.Length, mData, ocol.DataAddress);

            }
            else
                throw new Exception("Invalid data type. Column is of '" + ocol.ColumnType.ToString() + "' type, not date.");


        }


        /// <summary>
        /// Clears all data in the record.
        /// </summary>
        public void Clear()
        {
            Buffer.BlockCopy(mEmptyRecord, 0, mData, 0, mEmptyRecord.Length);
            mRecordIndex = -1;

        }


        /// <summary>
        /// returns a string representation of this record.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return new string(encoding.GetChars(mData));
        }


        /// <summary>
        /// Gets/sets a zero based record index. This information is not directly stored in DBF. 
        /// It is the location of this record within the DBF. 
        /// </summary>
        /// <remarks>
        /// This property is managed from outside this object,
        /// CDbfFile object updates it when records are read. The reason we don't set it in the Read() 
        /// function within this object is that the stream can be forward-only so the Position property 
        /// is not available and there is no way to figure out what index the record was unless you 
        /// count how many records were read, and that's exactly what CDbfFile does.
        /// </remarks>
        public int RecordIndex
        {
            get
            {
                return mRecordIndex;
            }
            set
            {
                mRecordIndex = value;
            }
        }


        /// <summary>
        /// Returns/sets flag indicating whether this record was tagged deleted. 
        /// </summary>
        /// <remarks>Use CDbf4File.Compress() function to rewrite dbf removing records flagged as deleted.</remarks>
        /// <seealso cref="CDbf4File.Compress() function"/>
        public bool IsDeleted
        {
            get { return mData[0] == '*'; }
            set { mData[0] = value ? (byte)'*' : (byte)' '; }
        }


        /// <summary>
        /// Specifies whether strings can be truncated. If false and string is longer than can fit in the field, an exception is thrown.
        /// Default is True.
        /// </summary>
        public bool AllowStringTurncate
        {
            get { return mAllowStringTruncate; }
            set { mAllowStringTruncate = value; }
        }

        /// <summary>
        /// Specifies whether to allow the decimal portion of numbers to be truncated. 
        /// If false and decimal digits overflow the field, an exception is thrown. Default is false.
        /// </summary>
        public bool AllowDecimalTruncate
        {
            get { return mAllowDecimalTruncate; }
            set { mAllowDecimalTruncate = value; }
        }


        /// <summary>
        /// Specifies whether integer portion of numbers can be truncated.
        /// If false and integer digits overflow the field, an exception is thrown. 
        /// Default is False.
        /// </summary>
        public bool AllowIntegerTruncate
        {
            get { return mAllowIntegerTruncate; }
            set { mAllowIntegerTruncate = value; }
        }


        /// <summary>
        /// Returns header object associated with this record.
        /// </summary>
        public DbfHeader Header
        {
            get
            {
                return mHeader;
            }
        }


        /// <summary>
        /// Get column by index.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public DbfColumn Column(int index)
        {
            return mHeader[index];
        }

        /// <summary>
        /// Get column by name.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public DbfColumn Column(string sName)
        {
            return mHeader[sName];
        }

        /// <summary>
        /// Gets column count from header.
        /// </summary>
        public int ColumnCount
        {
            get
            {
                return mHeader.ColumnCount;
            }
        }

        /// <summary>
        /// Finds a column index by searching sequentially through the list. Case is ignored. Returns -1 if not found.
        /// </summary>
        /// <param name="sName">Column name.</param>
        /// <returns>Column index (0 based) or -1 if not found.</returns>
        public int FindColumn(string sName)
        {
            return mHeader.FindColumn(sName);
        }

        /// <summary>
        /// Writes data to stream. Make sure stream is positioned correctly because we simply write out the data to it.
        /// </summary>
        /// <param name="osw"></param>
        protected internal void Write(Stream osw)
        {
            osw.Write(mData, 0, mData.Length);

        }


        /// <summary>
        /// Writes data to stream. Make sure stream is positioned correctly because we simply write out data to it, and clear the record.
        /// </summary>
        /// <param name="osw"></param>
        protected internal void Write(Stream obw, bool bClearRecordAfterWrite)
        {
            obw.Write(mData, 0, mData.Length);

            if (bClearRecordAfterWrite)
                Clear();

        }


        /// <summary>
        /// Read record from stream. Returns true if record read completely, otherwise returns false.
        /// </summary>
        /// <param name="obr"></param>
        /// <returns></returns>
        protected internal bool Read(Stream obr)
        {
            return obr.Read(mData, 0, mData.Length) >= mData.Length;
        }

        protected internal string ReadValue(Stream obr, int colIndex)
        {
            DbfColumn ocol = mHeader[colIndex];
            return new string(encoding.GetChars(mData, ocol.DataAddress, ocol.Length));

        }

    }
}
View Code

 

第三步:創建DBF檔案

  public void CreateDBF(string dbfPath, string DBFName)
        {
            var odbf = new DbfFile(Encoding.GetEncoding(936));
            odbf.Open(Path.Combine(dbfPath, DBFName + ".dbf"), FileMode.Create);

            //創建列頭
            odbf.Header.AddColumn(new DbfColumn("YHBH", DbfColumn.DbfColumnType.Character, 20, 0));
            odbf.Header.AddColumn(new DbfColumn("SBBH", DbfColumn.DbfColumnType.Character, 20, 0));
            odbf.Header.AddColumn(new DbfColumn("YHMC", DbfColumn.DbfColumnType.Character, 64, 0));

            odbf.Header.AddColumn(new DbfColumn("YHZZ", DbfColumn.DbfColumnType.Character, 100, 0));
            odbf.Header.AddColumn(new DbfColumn("SBWZ", DbfColumn.DbfColumnType.Character, 100, 0));
            odbf.Header.AddColumn(new DbfColumn("DH", DbfColumn.DbfColumnType.Character, 50, 0));
            odbf.Header.AddColumn(new DbfColumn("YDDH", DbfColumn.DbfColumnType.Character, 50, 0));
            odbf.Header.AddColumn(new DbfColumn("CBBH", DbfColumn.DbfColumnType.Character, 8, 0));
            odbf.Header.AddColumn(new DbfColumn("CBXH", DbfColumn.DbfColumnType.Number, 8, 0));
            odbf.Header.AddColumn(new DbfColumn("YSXZ", DbfColumn.DbfColumnType.Number, 12, 0));
            odbf.Header.AddColumn(new DbfColumn("SBQD", DbfColumn.DbfColumnType.Number, 12, 0));
            odbf.Header.AddColumn(new DbfColumn("BJSL", DbfColumn.DbfColumnType.Number, 12, 0));
            odbf.Header.AddColumn(new DbfColumn("SBZD", DbfColumn.DbfColumnType.Number, 12, 0));
            odbf.Header.AddColumn(new DbfColumn("SJYS", DbfColumn.DbfColumnType.Number, 12, 0));
            odbf.Header.AddColumn(new DbfColumn("CBRQ", DbfColumn.DbfColumnType.Character, 20, 0));
            odbf.Header.AddColumn(new DbfColumn("SBYXZT", DbfColumn.DbfColumnType.Character, 30, 0));
            odbf.Header.AddColumn(new DbfColumn("SFGS", DbfColumn.DbfColumnType.Character, 1, 0));
            odbf.Header.AddColumn(new DbfColumn("CBBZ", DbfColumn.DbfColumnType.Character, 1, 0));
            odbf.Header.AddColumn(new DbfColumn("SBKJ", DbfColumn.DbfColumnType.Number, 8, 0));
            odbf.Header.AddColumn(new DbfColumn("QYL", DbfColumn.DbfColumnType.Number, 8, 0));
            odbf.Header.AddColumn(new DbfColumn("QFJE", DbfColumn.DbfColumnType.Number, 15, 2));
            odbf.Header.AddColumn(new DbfColumn("YHDJ", DbfColumn.DbfColumnType.Number, 8, 2));
            odbf.Header.AddColumn(new DbfColumn("SCCBRQ", DbfColumn.DbfColumnType.Character, 20, 0));
            odbf.Header.AddColumn(new DbfColumn("SCSL", DbfColumn.DbfColumnType.Number, 12, 0));
            odbf.Header.AddColumn(new DbfColumn("SCCJD", DbfColumn.DbfColumnType.Number, 12, 0));
            odbf.Header.AddColumn(new DbfColumn("ISUPDATE", DbfColumn.DbfColumnType.Character, 1, 0));

第四步:將資料寫入DBF

        var orec = new DbfRecord(odbf.Header) { AllowDecimalTruncate = true };

            foreach (var item in WriteDTOlist)
            {
                orec[0] = item.YHBH;
                orec[1] = item.SBBH;
                orec[2] = item.YHMC;
                orec[3] = item.YHZZ;
                orec[4] = item.SBWZ;
                orec[5] = item.CBBH;
                orec[6] = item.CBXH.ToString();
                orec[7] = item.YSXZ;
                orec[8] = item.SBQD.ToString();
                orec[9] = item.BJSL.ToString();
                orec[10] = item.SBZD.ToString();
                orec[11] = item.SJYS.ToString();
                orec[12] = item.CBRQ;
                orec[13] = item.SBYXZT;
                orec[14] = item.SFGS;
                orec[15] = item.CBBZ;
               orec[16] = item.QYL.ToString();
                orec[17] = item.YHDJ.ToString(); 
                odbf.Write(orec, true);
            } 
            odbf.Close();

WriteDTOlist: List<WriteDTO> WriteDTOlist = new List<WriteDTO>();

 

讀取DBF,將DBF轉化為Datatable

          /// <summary>
        /// 從DBF讀取檔案到DataTable
        /// </summary>
        /// <param name="fileName">DBF的完整路徑:如E:\2222.dbf</param>
        /// <returns></returns>
        public static DataTable DbfToDataTable(string fileName)
        {
            try
            {
                //回傳的結果集
                DataTable dt = new DataTable();
                //獲取一個DBF檔案物件
                DbfFile dbf = new DbfFile(Encoding.Default);
                dbf.Open(fileName, FileMode.Open);

                //創建DataTable的結構(列名)
                DbfHeader dh = dbf.Header;
                for (int index = 0; index < dh.ColumnCount; index++)
                {
                    dt.Columns.Add(dh[index].Name);
                }

                //加載資料到DataTable里
                int i = 0;
                while (dbf.Read(i) != null)
                {
                    //獲取一行
                    DbfRecord record = dbf.Read(i);
                    //將改行資料放到DataRow里
                    DataRow dr = dt.NewRow();
                    Object[] objs = new Object[record.ColumnCount];
                    for (int index = 0; index < record.ColumnCount; index++)
                    {
                        objs[index] = record[index];
                    }
                    dr.ItemArray = objs;
                    dt.Rows.Add(dr);
                    i++;
                }
                dbf.Close();
                return dt;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }

        }

 

 

WriteDTO模型:

View Code

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/175748.html

標籤:其他

上一篇:C# 中 ConfigureAwait 相關答疑FAQ

下一篇:.NET Core開發的iNeuOS工業互聯平臺,iNeuKernel物聯網核心組件在Docker容器中部署。

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more