时间:2021-07-01 10:21:17 帮助过:3人阅读
生成的文件截图:
操作DBF文件的部分代码:
1 /// 2 /// Author: Ahmed Lacevic 3 /// Date: 12/1/2007 4 /// Desc: 5 /// 6 /// Revision History: 7 /// ----------------------------------- 8 /// Author: 9 /// Date: 10 /// Desc: 11 12 13 using System; 14 using System.Collections.Generic; 15 using System.Text; 16 using System.IO; 17 using System.Globalization; 18 19 20 namespace SocialExplorer.IO.FastDBF 21 { 22 23 /// <summary> 24 /// Use this class to create a record and write it to a dbf file. You can use one record object to write all records!! 25 /// It was designed for this kind of use. You can do this by clearing the record of all data 26 /// (call Clear() method) or setting values to all fields again, then write to dbf file. 27 /// This eliminates creating and destroying objects and optimizes memory use. 28 /// 29 /// Once you create a record the header can no longer be modified, since modifying the header would make a corrupt DBF file. 30 /// </summary> 31 public class DbfRecord 32 { 33 34 /// <summary> 35 /// Header provides information on all field types, sizes, precision and other useful information about the DBF. 36 /// </summary> 37 private DbfHeader mHeader = null; 38 39 /// <summary> 40 /// Dbf data are a mix of ASCII characters and binary, which neatly fit in a byte array. 41 /// BinaryWriter would esentially perform the same conversion using the same Encoding class. 42 /// </summary> 43 private byte[] mData = null; 44 45 /// <summary> 46 /// Zero based record index. -1 when not set, new records for example. 47 /// </summary> 48 private int mRecordIndex = -1; 49 50 /// <summary> 51 /// Empty Record array reference used to clear fields quickly (or entire record). 52 /// </summary> 53 private readonly byte[] mEmptyRecord = null; 54 55 56 /// <summary> 57 /// 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. 58 /// </summary> 59 private bool mAllowStringTruncate = true; 60 61 /// <summary> 62 /// Specifies whether we allow the decimal portion of numbers to be truncated. 63 /// If false and decimal digits overflow the field, an exception is thrown. 64 /// </summary> 65 private bool mAllowDecimalTruncate = false; 66 67 /// <summary> 68 /// Specifies whether we allow the integer portion of numbers to be truncated. 69 /// If false and integer digits overflow the field, an exception is thrown. 70 /// </summary> 71 private bool mAllowIntegerTruncate = false; 72 73 74 //array used to clear decimals, we can clear up to 40 decimals which is much more than is allowed under DBF spec anyway. 75 //Note: 48 is ASCII code for 0. 76 private static readonly byte[] mDecimalClear = new byte[] {48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, 77 48,48,48,48,48,48,48,48,48,48,48,48,48,48,48, 78 48,48,48,48,48,48,48,48,48,48,48,48,48,48,48}; 79 80 81 //Warning: do not make this one static because that would not be thread safe!! The reason I have 82 //placed this here is to skip small memory allocation/deallocation which fragments memory in .net. 83 private int[] mTempIntVal = { 0 }; 84 85 86 //Ascii Encoder 87 private readonly Encoding encoding = Encoding.ASCII; 88 89 /// <summary> 90 /// Column Name to Column Index map 91 /// </summary> 92 private readonly Dictionary<string, int> mColNameToConIdx = new Dictionary<string, int>(StringComparer.InvariantCulture); 93 94 95 96 /// <summary> 97 /// 98 /// </summary> 99 /// <param name="oHeader">Dbf Header will be locked once a record is created 100 /// since the record size is fixed and if the header was modified it would corrupt the DBF file.</param> 101 public DbfRecord(DbfHeader oHeader) 102 { 103 mHeader = oHeader; 104 mHeader.Locked = true; 105 106 //create a buffer to hold all record data. We will reuse this buffer to write all data to the file. 107 mData = new byte[mHeader.RecordLength]; 108 mEmptyRecord = mHeader.EmptyDataRecord; 109 encoding = oHeader.encoding; 110 111 for (int i = 0; i < oHeader.mFields.Count; i++) 112 mColNameToConIdx[oHeader.mFields[i].Name] = i; 113 } 114 115 116 /// <summary> 117 /// Set string data to a column, if the string is longer than specified column length it will be truncated! 118 /// If dbf column type is not a string, input will be treated as dbf column 119 /// type and if longer than length an exception will be thrown. 120 /// </summary> 121 /// <param name="nColIndex"></param> 122 /// <returns></returns> 123 public string this[int nColIndex] 124 { 125 126 set 127 { 128 129 DbfColumn ocol = mHeader[nColIndex]; 130 DbfColumn.DbfColumnType ocolType = ocol.ColumnType; 131 132 133 // 134 //if an empty value is passed, we just clear the data, and leave it blank. 135 //note: test have shown that testing for null and checking length is faster than comparing to "" empty str :) 136 //------------------------------------------------------------------------------------------------------------ 137 if (string.IsNullOrEmpty(value)) 138 { 139 //this is like NULL data, set it to empty. i looked at SAS DBF output when a null value exists 140 //and empty data are output. we get the same result, so this looks good. 141 Buffer.BlockCopy(mEmptyRecord, ocol.DataAddress, mData, ocol.DataAddress, ocol.Length); 142 143 } 144 else 145 { 146 147 //set values according to data type: 148 //------------------------------------------------------------- 149 if (ocolType == DbfColumn.DbfColumnType.Character) 150 { 151 if (!mAllowStringTruncate && value.Length > ocol.Length) 152 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."); 153 154 //BlockCopy copies bytes. First clear the previous value, then set the new one. 155 Buffer.BlockCopy(mEmptyRecord, ocol.DataAddress, mData, ocol.DataAddress, ocol.Length); 156 encoding.GetBytes(value, 0, value.Length > ocol.Length ? ocol.Length : value.Length, mData, ocol.DataAddress); 157 158 } 159 else if (ocolType == DbfColumn.DbfColumnType.Number) 160 { 161 162 if (ocol.DecimalCount == 0) 163 { 164 165 //integers 166 //---------------------------------- 167 168 //throw an exception if integer overflow would occur 169 if (!mAllowIntegerTruncate && value.Length > ocol.Length) 170 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."); 171 172 173 //clear all numbers, set to [space]. 174 //----------------------------------------------------- 175 Buffer.BlockCopy(mEmptyRecord, 0, mData, ocol.DataAddress, ocol.Length); 176 177 178 //set integer part, CAREFUL not to overflow buffer! (truncate instead) 179 //----------------------------------------------------------------------- 180 int nNumLen = value.Length > ocol.Length ? ocol.Length : value.Length; 181 encoding.GetBytes(value, 0, nNumLen, mData, (ocol.DataAddress + ocol.Length - nNumLen)); 182 183 } 184 else 185 { 186 187 ///TODO: we can improve perfomance here by not using temp char arrays cDec and cNum, 188 ///simply direcly copy from source string using encoding! 189 190 191 //break value down into integer and decimal portions 192 //-------------------------------------------------------------------------- 193 int nidxDecimal = value.IndexOf(‘.‘); //index where the decimal point occurs 194 char[] cDec = null; //decimal portion of the number 195 char[] cNum = null; //integer portion 196 197 if (nidxDecimal > -1) 198 { 199 cDec = value.Substring(nidxDecimal + 1).Trim().ToCharArray(); 200 cNum = value.Substring(0, nidxDecimal).ToCharArray(); 201 202 //throw an exception if decimal overflow would occur 203 if (!mAllowDecimalTruncate && cDec.Length > ocol.DecimalCount) 204 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."); 205 206 } 207 else 208 cNum = value.ToCharArray(); 209 210 211 //throw an exception if integer overflow would occur 212 if (!mAllowIntegerTruncate && cNum.Length > ocol.Length - ocol.DecimalCount - 1) 213 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."); 214 215 216 217 //clear all decimals, set to 0. 218 //----------------------------------------------------- 219 Buffer.BlockCopy(mDecimalClear, 0, mData, (ocol.DataAddress + ocol.Length - ocol.DecimalCount), ocol.DecimalCount); 220 221 //clear all numbers, set to [space]. 222 Buffer.BlockCopy(mEmptyRecord, 0, mData, ocol.DataAddress, (ocol.Length - ocol.DecimalCount)); 223 224 225 226 //set decimal numbers, CAREFUL not to overflow buffer! (truncate instead) 227 //----------------------------------------------------------------------- 228 if (nidxDecimal > -1) 229 { 230 int nLen = cDec.Length > ocol.DecimalCount ? ocol.DecimalCount : cDec.Length; 231 encoding.GetBytes(cDec, 0, nLen, mData, (ocol.DataAddress + ocol.Length - ocol.DecimalCount)); 232 } 233 234 //set integer part, CAREFUL not to overflow buffer! (truncate instead) 235 //----------------------------------------------------------------------- 236 int nNumLen = cNum.Length > ocol.Length - ocol.DecimalCount - 1 ? (ocol.Length - ocol.DecimalCount - 1) : cNum.Length; 237 encoding.GetBytes(cNum, 0, nNumLen, mData, ocol.DataAddress + ocol.Length - ocol.DecimalCount - nNumLen - 1); 238 239 240 //set decimal point 241 //----------------------------------------------------------------------- 242 mData[ocol.DataAddress + ocol.Length - ocol.DecimalCount - 1] = (byte)‘.‘; 243 244 245 } 246 247 248 } 249 else if (ocolType == DbfColumn.DbfColumnType.Integer) 250 { 251 //note this is a binary Integer type! 252 //---------------------------------------------- 253 254 ///TODO: maybe there is a better way to copy 4 bytes from int to byte array. Some memory function or something. 255 mTempIntVal[0] = Convert.ToInt32(value); 256 Buffer.BlockCopy(mTempIntVal, 0, mData, ocol.DataAddress, 4); 257 258 } 259 else if (ocolType == DbfColumn.DbfColumnType.Memo) 260 { 261 //copy 10 digits... 262 ///TODO: implement MEMO 263 264 throw new NotImplementedException("Memo data type functionality not implemented yet!"); 265 266 } 267 else if (ocolType == DbfColumn.DbfColumnType.Boolean) 268 { 269 if (String.Compare(value, "true", true) == 0 || String.Compare(value, "1", true) == 0 || 270 String.Compare(value, "T", true) == 0 || String.Compare(value, "yes", true) == 0 || 271 String.Compare(value, "Y", true) == 0) 272 mData[ocol.DataAddress] = (byte)‘T‘; 273 else if (value == " " || value == "?") 274 mData[ocol.DataAddress] = (byte)‘?‘; 275 else 276 mData[ocol.DataAddress] = (byte)‘F‘; 277 278 } 279 else if (ocolType == DbfColumn.DbfColumnType.Date) 280 { 281 //try to parse out date value using Date.Parse() function, then set the value 282 DateTime dateval; 283 if (DateTime.TryParse(value, out dateval)) 284 { 285 SetDateValue(nColIndex, dateval); 286 } 287 else 288 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)."); 289 290 } 291 else if (ocolType == DbfColumn.DbfColumnType.Binary) 292 throw new InvalidOperationException("Can not use string source to set binary data. Use SetBinaryValue() and GetBinaryValue() functions instead."); 293 294 else 295 throw new InvalidDataException("Unrecognized data type: " + ocolType.ToString()); 296 297 } 298 299 } 300 301 get 302 { 303 DbfColumn ocol = mHeader[nColIndex]; 304 return new string(encoding.GetChars(mData, ocol.DataAddress, ocol.Length)); 305 306 } 307 } 308 309 /// <summary> 310 /// Set string data to a column, if the string is longer than specified column length it will be truncated! 311 /// If dbf column type is not a string, input will be treated as dbf column 312 /// type and if longer than length an exception will be thrown. 313 /// </summary> 314 /// <param name="nColName"></param> 315 /// <returns></returns> 316 public string this[string nColName] 317 { 318 get 319 { 320 if (mColNameToConIdx.ContainsKey(nColName)) 321 return this[mColNameToConIdx[nColName]]; 322 throw new InvalidOperationException(string.Format("There‘s no column with name ‘{0}‘", nColName)); 323 } 324 set 325 { 326 if (mColNameToConIdx.ContainsKey(nColName)) 327 this[mColNameToConIdx[nColName]] = value; 328 else 329 throw new InvalidOperationException(string.Format("There‘s no column with name ‘{0}‘", nColName)); 330 } 331 } 332 333 /// <summary> 334 /// Get date value. 335 /// </summary> 336 ///