2023年12月22日发(作者:)

54

55

56 ///

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 /// 59 private bool mAllowStringTruncate = true; 60

61 ///

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 ///

65 private bool mAllowDecimalTruncate = false; 66

67 ///

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 /// 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 = ; 88

89 ///

90 /// Column Name to Column Index map 91 /// 92 private readonly Dictionary mColNameToConIdx = new Dictionary(antCulture); 93

94

95

96 ///

97 ///

98 ///

99 /// 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.101 public DbfRecord(DbfHeader oHeader)102 {103 mHeader = oHeader;104 = 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[Length];108 mEmptyRecord = ataRecord;109 encoding = ng;110

111 for (int i = 0; i < ; i++)112 mColNameToConIdx[s[i].Name] = i;113 }114

115

116 ///

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.

119 /// type and if longer than length an exception will be thrown.120 ///

121 /// 122 /// 123 public string this[int nColIndex]124 {125

126 set127 {128

129 DbfColumn ocol = mHeader[nColIndex];130 umnType ocolType = Type;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 (OrEmpty(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 opy(mEmptyRecord, dress, mData, dress, );142

143 }144 else145 {146

147 //set values according to data type:148 //-------------------------------------------------------------149 if (ocolType == ter)150 {151 if (!mAllowStringTruncate && > )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 opy(mEmptyRecord, dress, mData, dress, );156 es(value, 0, > ? : , mData, dress);157

158 }159 else if (ocolType == )160 {161

162 if (lCount == 0)163 {164

165 //integers166 //----------------------------------167

168 //throw an exception if integer overflow would occur169 if (!mAllowIntegerTruncate && > )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 opy(mEmptyRecord, 0, mData, dress, );176

177

178 //set integer part, CAREFUL not to overflow buffer! (truncate instead)179 //-----------------------------------------------------------------------180 int nNumLen = > ? : ;181 es(value, 0, nNumLen, mData, (dress + - nNumLen));182

183 }184 else185 {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 portions192 //--------------------------------------------------------------------------193 int nidxDecimal = f('.'); //index where the decimal point occurs194 char[] cDec = null; //decimal portion of the number195 char[] cNum = null; //integer portion196

197 if (nidxDecimal > -1)198 {199 cDec = ing(nidxDecimal + 1).Trim().ToCharArray();200 cNum = ing(0, nidxDecimal).ToCharArray();201

202 //throw an exception if decimal overflow would occur203 if (!mAllowDecimalTruncate && > lCount)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 else208 cNum = Array();209

210

211 //throw an exception if integer overflow would occur212 if (!mAllowIntegerTruncate && > - lCount - 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 opy(mDecimalClear, 0, mData, (dress + - lCount), lCount);220

221 //clear all numbers, set to [space].222 opy(mEmptyRecord, 0, mData, dress, ( - lCount));223

224

225

226 //set decimal numbers, CAREFUL not to overflow buffer! (truncate instead)227 //-----------------------------------------------------------------------228 if (nidxDecimal > -1)229 {230 int nLen = > lCount ? lCount : ;231 es(cDec, 0, nLen, mData, (dress + - lCount));232 }233

234 //set integer part, CAREFUL not to overflow buffer! (truncate instead)235 //-----------------------------------------------------------------------236 int nNumLen = > - lCount - 1 ? ( - lCount - 1) : ;237 es(cNum, 0, nNumLen, mData, dress + - lCount - nNumLen - 1);238

239

240 //set decimal point241 //-----------------------------------------------------------------------242 mData[dress + - lCount - 1] = (byte)'.';243

244

245 }

246

247

248 }249 else if (ocolType == r)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] = 32(value);256 opy(mTempIntVal, 0, mData, dress, 4);257

258 }259 else if (ocolType == )260 {261 //copy 262 ///TODO: implement MEMO263

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

266 }267 else if (ocolType == n)268 {269 if (e(value, "true", true) == 0 || e(value, "1", true) == 0 ||270 e(value, "T", true) == 0 || e(value, "yes", true) == 0 ||271 e(value, "Y", true) == 0)272 mData[dress] = (byte)'T';273 else if (value == " " || value == "?")274 mData[dress] = (byte)'?';275 else276 mData[dress] = (byte)'F';277

278 }279 else if (ocolType == )280 {281 //try to parse out date value using () function, then set the value282 DateTime dateval;283 if (se(value, out dateval))284 {285 SetDateValue(nColIndex, dateval);286 }287 else288 throw new InvalidOperationException("Date could not be parsed from source string! Please parse the Date and set the value (you

can try using () or se() functions).");289

290 }291 else if (ocolType == )292 throw new InvalidOperationException("Can not use string source to set binary data. Use SetBinaryValue() and GetBinaryValue()

functions instead.");293

294 else295 throw new InvalidDataException("Unrecognized data type: " + ng());296

297 }298

299 }300

301 get302 {303 DbfColumn ocol = mHeader[nColIndex];304 return new string(rs(mData, dress, ));305

306 }307 }308

309 ///

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 ///

314 /// 315 /// 316 public string this[string nColName]317 {318 get319 {320 if (nsKey(nColName))321 return this[mColNameToConIdx[nColName]];322 throw new InvalidOperationException(("There's no column with name '{0}'", nColName));323 }324 set325 {326 if (nsKey(nColName))327 this[mColNameToConIdx[nColName]] = value;328 else329 throw new InvalidOperationException(("There's no column with name '{0}'", nColName));330 }331 }332

333 ///

334 /// Get date value.335 /// 336 /// 337 /// 338 public DateTime GetDateValue(int nColIndex)339 {340 DbfColumn ocol = mHeader[nColIndex];341

342 if (Type == )343 {344 string sDateVal = ing(mData, dress, );345 return xact(sDateVal, "yyyyMMdd", antCulture);346

347 }348 else349 throw new Exception("Invalid data type. Column '" + + "' is not a date column.");350

351 }352

353

354 ///

355 /// Get date value.356 /// 357 /// 358 /// 359 public void SetDateValue(int nColIndex, DateTime value)360 {361

362 DbfColumn ocol = mHeader[nColIndex];363 umnType ocolType = Type;364

365

366 if (ocolType == )367 {368

369 //Format date and set value, date format is like this: yyyyMMdd370 //-------------------------------------------------------------371 es(ng("yyyyMMdd"), 0, , mData, dress);372

373 }

374 else375 throw new Exception("Invalid data type. Column is of '" + ng() + "' type, not date.");376

377

378 }379

380

381 ///

382 /// Clears all data in the record.383 /// 384 public void Clear()385 {386 opy(mEmptyRecord, 0, mData, 0, );387 mRecordIndex = -1;388

389 }390

391

392 ///

393 /// returns a string representation of this record.394 /// 395 /// 396 public override string ToString()397 {398 return new string(rs(mData));399 }400

401

402 ///

403 /// Gets/sets a zero based record index. This information is not directly stored in DBF.

404 /// It is the location of this record within the DBF.

405 ///

406 /// 407 /// This property is managed from outside this object,408 /// CDbfFile object updates it when records are read. The reason we don't set it in the Read()

409 /// function within this object is that the stream can be forward-only so the Position property

410 /// is not available and there is no way to figure out what index the record was unless you

411 /// count how many records were read, and that's exactly what CDbfFile does.412 /// 413 public int RecordIndex414 {415 get416 {417 return mRecordIndex;418 }419 set420 {421 mRecordIndex = value;422 }423 }424

425

426 ///

427 /// Returns/sets flag indicating whether this record was tagged deleted.

428 ///

429 /// Use ss() function to rewrite dbf removing records flagged as deleted.430 /// 431 public bool IsDeleted432 {433 get { return mData[0] == '*'; }434 set { mData[0] = value ? (byte)'*' : (byte)' '; }435 }436

437

438 ///

439 /// Specifies whether strings can be truncated. If false and string is longer than can fit in the field, an exception is thrown.440 /// Default is True.441 ///

442 public bool AllowStringTurncate443 {444 get { return mAllowStringTruncate; }445 set { mAllowStringTruncate = value; }446 }447

448 ///

449 /// Specifies whether to allow the decimal portion of numbers to be truncated.

450 /// If false and decimal digits overflow the field, an exception is thrown. Default is false.451 ///

452 public bool AllowDecimalTruncate453 {454 get { return mAllowDecimalTruncate; }455 set { mAllowDecimalTruncate = value; }456 }457

458

459 ///

460 /// Specifies whether integer portion of numbers can be truncated.461 /// If false and integer digits overflow the field, an exception is thrown.

462 /// Default is False.463 ///

464 public bool AllowIntegerTruncate465 {466 get { return mAllowIntegerTruncate; }467 set { mAllowIntegerTruncate = value; }468 }469

470

471 ///

472 /// Returns header object associated with this record.473 /// 474 public DbfHeader Header475 {476 get477 {478 return mHeader;479 }480 }481

482

483 ///

484 /// Get column by index.485 /// 486 /// 487 /// 488 public DbfColumn Column(int index)489 {490 return mHeader[index];491 }492

493 ///

494 /// Get column by name.495 /// 496 /// 497 /// 498 public DbfColumn Column(string sName)499 {500 return mHeader[sName];501 }502

503 ///

504 /// Gets column count from header.

504 /// Gets column count from header.505 ///

506 public int ColumnCount507 {508 get509 {510 return Count;511 }512 }513

514 ///

515 /// Finds a column index by searching sequentially through the list. Case is ignored. Returns -1 if not found.516 /// 517 /// Column name.518 /// Column index (0 based) or -1 if not found.519 public int FindColumn(string sName)520 {521 return lumn(sName);522 }523

524 ///

525 /// Writes data to stream. Make sure stream is positioned correctly because we simply write out the data to it.526 /// 527 /// 528 protected internal void Write(Stream osw)529 {530 (mData, 0, );531

532 }533

534

535 ///

536 /// Writes data to stream. Make sure stream is positioned correctly because we simply write out data to it, and clear the record.537 /// 538 /// 539 protected internal void Write(Stream obw, bool bClearRecordAfterWrite)540 {541 (mData, 0, );542

543 if (bClearRecordAfterWrite)544 Clear();545

546 }547

548

549 ///

550 /// Read record from stream. Returns true if record read completely, otherwise returns false.551 /// 552 /// 553 /// 554 protected internal bool Read(Stream obr)555 {556 return (mData, 0, ) >= ;557 }558

559 protected internal string ReadValue(Stream obr, int colIndex)560 {561 DbfColumn ocol = mHeader[colIndex];562 return new string(rs(mData, dress, ));563

564 }565

566 }567 }

View Code

完整代码下载(含FastDBF源代码):