C#生成DBF文件,
C# 生成DBF,無需注冊Microsoft.Jet.OLEDB。
1 namespace ConsoleApplication
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 Test();
8 Console.ReadKey();
9 }
10
11 private static void Test()
12 {
13 string testPath = AppDomain.CurrentDomain.BaseDirectory;
14 var odbf = new DbfFile(Encoding.GetEncoding(936));
15 odbf.Open(Path.Combine(testPath, "test.dbf"), FileMode.Create);
16
17 //創建列頭
18 odbf.Header.AddColumn(new DbfColumn("編號", DbfColumn.DbfColumnType.Character, 20, 0));
19 odbf.Header.AddColumn(new DbfColumn("名稱", DbfColumn.DbfColumnType.Character, 20, 0));
20 odbf.Header.AddColumn(new DbfColumn("地址", DbfColumn.DbfColumnType.Character, 20, 0));
21 odbf.Header.AddColumn(new DbfColumn("時間", DbfColumn.DbfColumnType.Date));
22 odbf.Header.AddColumn(new DbfColumn("余額", DbfColumn.DbfColumnType.Number, 15, 3));
23
24 var orec = new DbfRecord(odbf.Header) { AllowDecimalTruncate = true };
25 List<User> list = User.GetList();
26 foreach (var item in list)
27 {
28 orec[0] = item.UserCode;
29 orec[1] = item.UserName;
30 orec[2] = item.Address;
31 orec[3] = item.date.ToString("yyyy-MM-dd HH:mm:ss");
32 orec[4] = item.money.ToString();
33 odbf.Write(orec, true);
34 }
35 odbf.Close();
36 }
37 }
38
39 public class User
40 {
41 public string UserCode { get; set; }
42 public string UserName { get; set; }
43 public string Address { get; set; }
44 public DateTime date { get; set; }
45 public decimal money { get; set; }
46
47 public static List<User> GetList()
48 {
49 List<User> list = new List<User>();
50 list.Add(new User() { UserCode = "A1", UserName = "張三", Address = "上海楊浦", date = DateTime.Now, money = 1000.12m });
51 list.Add(new User() { UserCode = "A2", UserName = "李四", Address = "湖北武漢", date = DateTime.Now, money = 31000.008m });
52 list.Add(new User() { UserCode = "A3", UserName = "王子龍", Address = "陝西西安", date = DateTime.Now, money = 2000.12m });
53 list.Add(new User() { UserCode = "A4", UserName = "李三", Address = "北京", date = DateTime.Now, money = 3000.12m });
54 return list;
55 }
56 }
57
58 }
生成的文件截圖:

操作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 /// <param name="nColIndex"></param>
337 /// <returns></returns>
338 public DateTime GetDateValue(int nColIndex)
339 {
340 DbfColumn ocol = mHeader[nColIndex];
341
342 if (ocol.ColumnType == DbfColumn.DbfColumnType.Date)
343 {
344 string sDateVal = encoding.GetString(mData, ocol.DataAddress, ocol.Length);
345 return DateTime.ParseExact(sDateVal, "yyyyMMdd", CultureInfo.InvariantCulture);
346
347 }
348 else
349 throw new Exception("Invalid data type. Column '" + ocol.Name + "' is not a date column.");
350
351 }
352
353
354 /// <summary>
355 /// Get date value.
356 /// </summary>
357 /// <param name="nColIndex"></param>
358 /// <returns></returns>
359 public void SetDateValue(int nColIndex, DateTime value)
360 {
361
362 DbfColumn ocol = mHeader[nColIndex];
363 DbfColumn.DbfColumnType ocolType = ocol.ColumnType;
364
365
366 if (ocolType == DbfColumn.DbfColumnType.Date)
367 {
368
369 //Format date and set value, date format is like this: yyyyMMdd
370 //-------------------------------------------------------------
371 encoding.GetBytes(value.ToString("yyyyMMdd"), 0, ocol.Length, mData, ocol.DataAddress);
372
373 }
374 else
375 throw new Exception("Invalid data type. Column is of '" + ocol.ColumnType.ToString() + "' type, not date.");
376
377
378 }
379
380
381 /// <summary>
382 /// Clears all data in the record.
383 /// </summary>
384 public void Clear()
385 {
386 Buffer.BlockCopy(mEmptyRecord, 0, mData, 0, mEmptyRecord.Length);
387 mRecordIndex = -1;
388
389 }
390
391
392 /// <summary>
393 /// returns a string representation of this record.
394 /// </summary>
395 /// <returns></returns>
396 public override string ToString()
397 {
398 return new string(encoding.GetChars(mData));
399 }
400
401
402 /// <summary>
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 /// </summary>
406 /// <remarks>
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 /// </remarks>
413 public int RecordIndex
414 {
415 get
416 {
417 return mRecordIndex;
418 }
419 set
420 {
421 mRecordIndex = value;
422 }
423 }
424
425
426 /// <summary>
427 /// Returns/sets flag indicating whether this record was tagged deleted.
428 /// </summary>
429 /// <remarks>Use CDbf4File.Compress() function to rewrite dbf removing records flagged as deleted.</remarks>
430 /// <seealso cref="CDbf4File.Compress() function"/>
431 public bool IsDeleted
432 {
433 get { return mData[0] == '*'; }
434 set { mData[0] = value ? (byte)'*' : (byte)' '; }
435 }
436
437
438 /// <summary>
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 /// </summary>
442 public bool AllowStringTurncate
443 {
444 get { return mAllowStringTruncate; }
445 set { mAllowStringTruncate = value; }
446 }
447
448 /// <summary>
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 /// </summary>
452 public bool AllowDecimalTruncate
453 {
454 get { return mAllowDecimalTruncate; }
455 set { mAllowDecimalTruncate = value; }
456 }
457
458
459 /// <summary>
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 /// </summary>
464 public bool AllowIntegerTruncate
465 {
466 get { return mAllowIntegerTruncate; }
467 set { mAllowIntegerTruncate = value; }
468 }
469
470
471 /// <summary>
472 /// Returns header object associated with this record.
473 /// </summary>
474 public DbfHeader Header
475 {
476 get
477 {
478 return mHeader;
479 }
480 }
481
482
483 /// <summary>
484 /// Get column by index.
485 /// </summary>
486 /// <param name="index"></param>
487 /// <returns></returns>
488 public DbfColumn Column(int index)
489 {
490 return mHeader[index];
491 }
492
493 /// <summary>
494 /// Get column by name.
495 /// </summary>
496 /// <param name="index"></param>
497 /// <returns></returns>
498 public DbfColumn Column(string sName)
499 {
500 return mHeader[sName];
501 }
502
503 /// <summary>
504 /// Gets column count from header.
505 /// </summary>
506 public int ColumnCount
507 {
508 get
509 {
510 return mHeader.ColumnCount;
511 }
512 }
513
514 /// <summary>
515 /// Finds a column index by searching sequentially through the list. Case is ignored. Returns -1 if not found.
516 /// </summary>
517 /// <param name="sName">Column name.</param>
518 /// <returns>Column index (0 based) or -1 if not found.</returns>
519 public int FindColumn(string sName)
520 {
521 return mHeader.FindColumn(sName);
522 }
523
524 /// <summary>
525 /// Writes data to stream. Make sure stream is positioned correctly because we simply write out the data to it.
526 /// </summary>
527 /// <param name="osw"></param>
528 protected internal void Write(Stream osw)
529 {
530 osw.Write(mData, 0, mData.Length);
531
532 }
533
534
535 /// <summary>
536 /// Writes data to stream. Make sure stream is positioned correctly because we simply write out data to it, and clear the record.
537 /// </summary>
538 /// <param name="osw"></param>
539 protected internal void Write(Stream obw, bool bClearRecordAfterWrite)
540 {
541 obw.Write(mData, 0, mData.Length);
542
543 if (bClearRecordAfterWrite)
544 Clear();
545
546 }
547
548
549 /// <summary>
550 /// Read record from stream. Returns true if record read completely, otherwise returns false.
551 /// </summary>
552 /// <param name="obr"></param>
553 /// <returns></returns>
554 protected internal bool Read(Stream obr)
555 {
556 return obr.Read(mData, 0, mData.Length) >= mData.Length;
557 }
558
559 protected internal string ReadValue(Stream obr, int colIndex)
560 {
561 DbfColumn ocol = mHeader[colIndex];
562 return new string(encoding.GetChars(mData, ocol.DataAddress, ocol.Length));
563
564 }
565
566 }
567 }
View Code
完整代碼下載(含FastDBF源代碼):下載