using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
namespace GisSharpBlog.NetTopologySuite.IO
{
///
/// Class that allows records in a dbase file to be enumerated.
///
public class DbaseFileReader : IEnumerable
{
private DbaseFileHeader _header = null;
private readonly string _filename;
#region Constructors
///
/// Initializes a new instance of the DbaseFileReader class.
///
///
public DbaseFileReader(string filename)
{
if (filename == null)
{
throw new ArgumentNullException(filename);
}
// check for the file existing here, otherwise we will not get an error
//until we read the first record or read the header.
if (!File.Exists(filename))
{
throw new FileNotFoundException(String.Format("Could not find file \"{0}\"", filename));
}
_filename = filename;
}
#endregion
#region Methods
///
/// Gets the header information for the dbase file.
///
/// DbaseFileHeader contain header and field information.
public DbaseFileHeader GetHeader()
{
if (_header == null)
{
FileStream stream = new FileStream(_filename, FileMode.Open, FileAccess.Read);
BinaryReader dbfStream = new BinaryReader(stream);
_header = new DbaseFileHeader();
// read the header
_header.ReadHeader(dbfStream);
dbfStream.Close();
stream.Close();
}
return _header;
}
#endregion
#region Implementation of IEnumerable
///
/// Gets the object that allows iterating through the members of the collection.
///
///
/// An object that implements the IEnumerator interface.
///
public IEnumerator GetEnumerator()
{
return new DbaseFileEnumerator(this);
}
#endregion
///
///
///
private class DbaseFileEnumerator : IEnumerator, IDisposable
{
protected string[] _fieldNames = null;
private DbaseFileReader _parent;
private ArrayList _arrayList;
private int _iCurrentRecord = 0;
private readonly BinaryReader _dbfStream = null;
private int _readPosition = 0;
private DbaseFileHeader _header = null;
///
/// Initializes a new instance of the class.
///
///
public DbaseFileEnumerator(DbaseFileReader parent)
{
_parent = parent;
FileStream stream = new FileStream(parent._filename, FileMode.Open, FileAccess.Read, FileShare.Read);
_dbfStream = new BinaryReader(stream, Encoding.Default);
ReadHeader();
}
#region IDisposable Members
///
/// Performs application-defined tasks associated with freeing, releasing,
/// or resetting unmanaged resources.
///
public void Dispose()
{
_dbfStream.Close();
}
#endregion
#region Implementation of IEnumerator
///
/// Sets the enumerator to its initial position, which is
/// before the first element in the collection.
///
///
/// The collection was modified after the enumerator was created.
///
public void Reset()
{
_dbfStream.BaseStream.Seek(_header.HeaderLength, SeekOrigin.Begin);
_iCurrentRecord = 0;
//throw new InvalidOperationException();
}
///
/// Advances the enumerator to the next element of the collection.
///
///
/// true if the enumerator was successfully advanced to the next element;
/// false if the enumerator has passed the end of the collection.
///
///
/// The collection was modified after the enumerator was created.
///
public bool MoveNext()
{
_iCurrentRecord++;
if (_iCurrentRecord <= _header.NumRecords)
{
_arrayList = Read();
}
bool more = true;
if (_iCurrentRecord > _header.NumRecords)
{
//this._dbfStream.Close();
more = false;
}
return more;
}
///
/// Gets the current element in the collection.
///
///
/// The current element in the collection.
///
/// The enumerator is positioned before the first element of the collection
/// or after the last element.
///
public object Current
{
get
{
return _arrayList;
}
}
///
///
///
protected void ReadHeader()
{
_header = new DbaseFileHeader();
// read the header
_header.ReadHeader(_dbfStream);
// how many records remain
_readPosition = _header.HeaderLength;
}
///
/// Read a single dbase record
///
///
/// The read shapefile record,
/// or null if there are no more records.
///
private ArrayList Read()
{
ArrayList attrs = null;
bool foundRecord = false;
while (!foundRecord)
{
// retrieve the record length
int tempNumFields = _header.NumFields;
// storage for the actual values
attrs = new ArrayList(tempNumFields);
// read the deleted flag
char tempDeleted = (char) _dbfStream.ReadChar();
// read the record length
int tempRecordLength = 1; // for the deleted character just read.
// read the Fields
for (int j = 0; j < tempNumFields; j++)
{
// find the length of the field.
int tempFieldLength = _header.Fields[j].Length;
tempRecordLength = tempRecordLength + tempFieldLength;
// find the field type
char tempFieldType = _header.Fields[j].DbaseType;
// read the data.
object tempObject = null;
switch (tempFieldType)
{
case 'L': // logical data type, one character (T,t,F,f,Y,y,N,n)
char tempChar = (char) _dbfStream.ReadByte();
if ((tempChar == 'T') || (tempChar == 't') || (tempChar == 'Y') || (tempChar == 'y'))
{
tempObject = true;
}
else
{
tempObject = false;
}
break;
case 'C': // character record.
char[] sbuffer = new char[tempFieldLength];
sbuffer = _dbfStream.ReadChars(tempFieldLength);
// use an encoding to ensure all 8 bits are loaded
// tempObject = new string(sbuffer, "ISO-8859-1").Trim();
//HACK: this can be made more efficient
tempObject = new string(sbuffer).Trim().Replace("\0", String.Empty); //.ToCharArray();
break;
case 'D': // date data type.
char[] ebuffer = new char[8];
ebuffer = _dbfStream.ReadChars(8);
string tempString = new string(ebuffer, 0, 4);
int year;
if (!Int32.TryParse(tempString, NumberStyles.Integer, CultureInfo.InvariantCulture, out year))
{
break;
}
tempString = new string(ebuffer, 4, 2);
int month;
if (!Int32.TryParse(tempString, NumberStyles.Integer, CultureInfo.InvariantCulture, out month))
{
break;
}
tempString = new string(ebuffer, 6, 2);
int day;
if (!Int32.TryParse(tempString, NumberStyles.Integer, CultureInfo.InvariantCulture, out day))
{
break;
}
tempObject = new DateTime(year, month, day);
break;
case 'N': // number
case 'F': // floating point number
char[] fbuffer = new char[tempFieldLength];
fbuffer = _dbfStream.ReadChars(tempFieldLength);
tempString = new string(fbuffer);
try
{
tempObject = Double.Parse(tempString.Trim(), CultureInfo.InvariantCulture);
}
catch (FormatException)
{
// if we can't format the number, just save it as a string
tempObject = tempString;
}
break;
default:
throw new NotSupportedException("Do not know how to parse Field type " + tempFieldType);
}
attrs.Add(tempObject);
}
// ensure that the full record has been read.
if (tempRecordLength < _header.RecordLength)
{
byte[] tempbuff = new byte[_header.RecordLength - tempRecordLength];
tempbuff = _dbfStream.ReadBytes(_header.RecordLength - tempRecordLength);
}
// add the row if it is not deleted.
if (tempDeleted != '*')
{
foundRecord = true;
}
}
return attrs;
}
#endregion
}
}
}