FastDBF源代碼地址:https://github.com/SocialExplorer/FastDBF 第一步在解決方案中新建一個類庫的項目:取名為SocialExplorer.FastDBF 第二步:引入FASTDBF的源文件 源代碼可以通過github地址下載引入 源文件:DbfColum ...
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) {