Files
zszq-trs/Framework/YLErp.Core/DotNetDBF/DBFReader.cs
T
2024-05-09 14:06:26 +08:00

357 lines
12 KiB
C#

/*
DBFReader
Class for reading the records assuming that the given
InputStream comtains DBF data.
This file is part of DotNetDBF packege.
original author (javadbf): anil@linuxense.com 2004/03/31
License: LGPL (http://www.gnu.org/copyleft/lesser.html)
ported to C# (DotNetDBF): Jay Tuley <jay+dotnetdbf@tuley.name> 6/28/2007
*/
using System.Text;
namespace DotNetDBF
{
/// <summary>
///
/// </summary>
public class DBFReader : DBFBase, IDisposable
{
private readonly BinaryReader _dataInputStream;
private readonly DBFHeader _header;
private readonly int _unusedSize;
/* Class specific variables */
private bool _isClosed = true;
/**
Initializes a DBFReader object.
When this constructor returns the object
will have completed reading the header (meta date) and
header information can be queried there on. And it will
be ready to return the first row.
@param InputStream where the data is read from.
*/
/// <summary>
///
/// </summary>
public DBFReader(string dbfFilePath)
{
try
{
_dataInputStream = new BinaryReader(
File.Open(dbfFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
);
_isClosed = false;
_header = new DBFHeader();
_header.Read(_dataInputStream);
_unusedSize = _header.RecordLength - _header.RecordSize();
/* it might be required to leap to the start of records at times */
var t_dataStartIndex = _header.HeaderLength
- (32 + (32 * _header.FieldArray.Length))
- 1;
if (t_dataStartIndex > 0)
{
_dataInputStream.ReadBytes((t_dataStartIndex));
}
}
catch (IOException ex)
{
throw new DBFException("Failed To Read DBF", ex);
}
}
/// <summary>
///
/// </summary>
public DBFReader(Stream stream)
{
try
{
_dataInputStream = new BinaryReader(stream);
_isClosed = false;
_header = new DBFHeader();
_header.Read(_dataInputStream);
_unusedSize = _header.RecordLength - _header.RecordSize();
/* it might be required to leap to the start of records at times */
var t_dataStartIndex = _header.HeaderLength
- (32 + (32 * _header.FieldArray.Length))
- 1;
if (t_dataStartIndex > 0)
{
_dataInputStream.ReadBytes((t_dataStartIndex));
}
}
catch (IOException e)
{
throw new DBFException("Failed To Read DBF", e);
}
}
/**
Returns the number of records in the DBF.
*/
public int RecordCount => _header.NumberOfRecords;
/**
Returns the asked Field. In case of an invalid index,
it returns a ArrayIndexOutOfBoundsException.
@param index. Index of the field. Index of the first field is zero.
*/
public DBFField[] Fields => _header.FieldArray;
#region IDisposable Members
/// <summary>Performs application-defined tasks associated with freeing, releasing,
/// or resetting unmanaged resources.</summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
Close();
}
#endregion
/// <summary>
///
/// </summary>
public delegate Stream LazyStream();
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString()
{
var sb =
new StringBuilder(_header.Year + "/" + _header.Month + "/"
+ _header.Day + "\n"
+ "Total records: " + _header.NumberOfRecords +
"\nHeader length: " + _header.HeaderLength +
"");
for (var i = 0; i < _header.FieldArray.Length; i++)
{
sb.Append(_header.FieldArray[i].Name);
sb.Append("\n");
}
return sb.ToString();
}
/// <summary>
///
/// </summary>
public void Close()
{
_dataInputStream.Close();
_isClosed = true;
}
/// <summary>
///
/// </summary>
public void SkipRecord(int count)
{
if (_isClosed)
{
throw new DBFException("Source is not open");
}
try
{
_dataInputStream.BaseStream.Seek(_header.RecordLength * count, SeekOrigin.Current);
}
catch (EndOfStreamException)
{
return;
}
}
/// <summary>
/// Reads the returns the next row in the DBF stream.
/// </summary>
/// <returns>返回null时不要再读取下一行数据</returns>
public string[] NextRecord()
{
if (_isClosed)
{
throw new DBFException("Source is not open");
}
try
{
int t_byte = _dataInputStream.ReadByte();
if (t_byte == DBFFieldType.EndOfData)
{
return null;
}
//0x2a--deleted
if (t_byte == '*')
{
_dataInputStream.BaseStream.Seek(_header.RecordLength - 1, SeekOrigin.Current);
return new string[0];
}
if (t_byte != 0x20)
{
return null;
}
var fieldArray = _header.FieldArray;
var recordObjects = new string[fieldArray.Length];
for (var i = 0; i < fieldArray.Length; i++)
{
switch (fieldArray[i].DataType)
{
case NativeDbType.Char:
var b_array = new byte[fieldArray[i].FieldLength];
_dataInputStream.Read(b_array, 0, b_array.Length);
recordObjects[i] = CharEncoding.GetString(b_array).Trim();
break;
case NativeDbType.Date:
var t_byte_year = new byte[4];
_dataInputStream.Read(t_byte_year, 0, t_byte_year.Length);
var t_byte_month = new byte[2];
_dataInputStream.Read(t_byte_month, 0, t_byte_month.Length);
var t_byte_day = new byte[2];
_dataInputStream.Read(t_byte_day, 0, t_byte_day.Length);
try
{
var tYear = CharEncoding.GetString(t_byte_year);
var tMonth = CharEncoding.GetString(t_byte_month);
var tDay = CharEncoding.GetString(t_byte_day);
recordObjects[i] = string.Join("", new[] { tYear, tMonth, tDay });
}
catch (ArgumentOutOfRangeException)
{
/* this field may be empty or may have improper value set */
recordObjects[i] = null;
}
break;
case NativeDbType.Float:
try
{
var t_float = new byte[fieldArray[i].FieldLength];
_dataInputStream.Read(t_float, 0, t_float.Length);
var tParsed = CharEncoding.GetString(t_float);
var tLast = tParsed.Substring(tParsed.Length - 1);
if (tParsed.Length > 0 && tLast != " " && tLast != NullSymbol)
{
//
// A Float in FoxPro has 20 significant digits, since it is
// stored as a string with possible E-postfix notation.
// An IEEE 754 float or double can not handle this number of digits
// correctly. Therefor the only correct implementation is to use a decimal.
//
recordObjects[i] = tParsed;
}
else
{
recordObjects[i] = null;
}
}
catch (FormatException e)
{
throw new DBFException("Failed to parse Float", e);
}
break;
case NativeDbType.Numeric:
try
{
var t_numeric = new byte[fieldArray[i].FieldLength];
_dataInputStream.Read(t_numeric, 0, t_numeric.Length);
var tParsed = CharEncoding.GetString(t_numeric);
var tLast = tParsed.Substring(tParsed.Length - 1);
if (tParsed.Length > 0 && tLast != " " && tLast != NullSymbol)
{
recordObjects[i] = tParsed;
}
else
{
recordObjects[i] = null;
}
}
catch (FormatException e)
{
throw new DBFException("Failed to parse Number", e);
}
break;
case NativeDbType.Logical:
var t_logical = _dataInputStream.ReadByte();
//todo find out whats really valid
if (t_logical == 'Y' || t_logical == 't' || t_logical == 'T' || t_logical == 't')
{
recordObjects[i] = "true";
}
else if (t_logical == DBFFieldType.UnknownByte)
{
recordObjects[i] = "";
}
else
{
recordObjects[i] = "false";
}
break;
case NativeDbType.Memo:
recordObjects[i] = "#memo";
break;
default:
{
var data = _dataInputStream.ReadBytes(fieldArray[i].FieldLength);
recordObjects[i] = data != null ? Convert.ToBase64String(data) : string.Empty;
break;
}
}
}
if (_unusedSize > 0)
{
_dataInputStream.BaseStream.Seek(_unusedSize, SeekOrigin.Current);
}
return recordObjects;
}
catch (EndOfStreamException)
{
return null;
}
catch (IOException e)
{
throw new DBFException("Problem Reading File", e);
}
}
}
}