/// <summary> /// Helper function that turns the list of records into a DataTable, based on a decoding function that turns the raw byte data into a String. /// </summary> /// <param name="decodeBytes">Function to decode an array of bytes to a string.</param> /// <param name="decodeField">Function to choose a C# .Net type for a DataColumn, based on a Starling field type.</param> /// <returns>DataTable populated with the database's data.</returns> private DataTable getDataTable(Func <byte[], String> decodeBytes, Func <StarlingDbfField.FieldType, Type> decodeField) { DataTable dt = new DataTable(table_name, "ns-" + table_name); // add a column for the deleted marker and populate it dt.Columns.Add("deleted", typeof(bool)); foreach (StarlingDbfRecord rec in records) { dt.Rows.Add(rec.record_status == StarlingDbfRecord.status.deleted); } // add other columns int cell = 0; for (int index_field = 0; index_field < header.field_structures.Count; index_field++) { // add column StarlingDbfField fld = header.field_structures[index_field]; Type coltype = decodeField(fld.type); DataColumn dc = new DataColumn(fld.name, coltype); dt.Columns.Add(dc); // populate column for (int index_rec = 0; index_rec < records.Count; index_rec++) { // get database data as string String s = decodeBytes(records[index_rec].field_contents[index_field]).Trim(); // add cell, with the data in the appropriate type dt.Rows[index_rec].SetField(dc, decodedDataToType(s, coltype)); // report decoding difficulties/progress if (s.Contains(StarlingDecoder.fallbackCharacter.ToString())) { Trace.WriteLine(String.Format("Check record {0}, field {1}.", index_rec + 1, index_field + 2)); } reportProgress((100d * ++cell) / (records.Count * header.field_structures.Count), "Decoding"); } } return(dt); }
/// <summary> /// Constructor method that opens and reads a Starling database (.dbf). /// </summary> /// <param name="dbfFilename">Filename of the Starling database to be read.</param> /// <param name="progressHandler">Optional event handler for progress reports.</param> public StarlingDbfReader(String dbfFilename, ReportProgressEventHandler progressHandler = null) { // register optional event if (progressHandler != null) { onProgressChanged += progressHandler; } // get var filename String VarFilename = Path.ChangeExtension(dbfFilename, "var"); table_name = Path.GetFileNameWithoutExtension(dbfFilename); if (File.Exists(dbfFilename)) { // read header with database structure reportProgress(0, "Reading header"); header = new StarlingDbfHeader(dbfFilename); reportProgress(10, "Reading header: fields"); // log header info Trace.WriteLine(dbfFilename); Trace.WriteLine(String.Format("DBF Filesize:\t\t{0}", header.getFileSize())); Trace.WriteLine(String.Format("Actual filesize:\t{0}", new FileInfo(dbfFilename).Length)); Trace.WriteLine("Header"); Trace.Indent(); foreach (String s in header.ToString().Split('\n')) { Trace.WriteLine(s); } Trace.Unindent(); Trace.WriteLine(String.Format("Fields ({0})", header.field_structures.Count)); Trace.Indent(); // read field structures for (int i = 0; i < header.field_structures.Count; i++) { StarlingDbfField rec = header.field_structures[i]; // log field info Trace.WriteLine(String.Format("Field {0}", i)); Trace.Indent(); foreach (String s in rec.ToString().Split('\n')) { Trace.WriteLine(s); } Trace.Unindent(); // report progress reportProgress(10 + 10 * ((float)i / header.field_structures.Count), "Reading header: fields"); } Trace.Unindent(); // read byte content for all records reportProgress(20, "Reading records"); records = new List <StarlingDbfRecord>(); using (BinaryReader dbfFile = new BinaryReader(File.OpenRead(dbfFilename), Encoding.ASCII)) { dbfFile.ReadBytes(header.HSZ); while (dbfFile.BaseStream.Length - dbfFile.BaseStream.Position >= header.DRS) { StarlingDbfRecord rec = new StarlingDbfRecord(header, dbfFile.ReadBytes(header.DRS)); records.Add(rec); reportProgress(20 + (40d * dbfFile.BaseStream.Position) / dbfFile.BaseStream.Length, "Reading records"); } } if (File.Exists(VarFilename)) { // in case of an accompanying .var file, replace byte content of record fields if they are pointers to data in .var file // first check if we are dealing with a .dbf file and if it contains data pointers bool varfile_is_needed = false; if (String.Equals(Path.GetExtension(dbfFilename), ".dbf", StringComparison.OrdinalIgnoreCase)) { for (int i = 0; i < header.field_structures.Count && !varfile_is_needed; i++) { StarlingDbfField fld = header.field_structures[i]; varfile_is_needed = (fld.type == StarlingDbfField.FieldType.character && fld.fieldlength == 6); } } if (varfile_is_needed) { reportProgress(60, "Processing records"); using (BinaryReader varFile = new BinaryReader(File.OpenRead(VarFilename), Encoding.ASCII)) { // walk through all fields (columns) for (int i = 0; i < header.field_structures.Count; i++) { // check if the field contains data pointers, if so update each record's contents StarlingDbfField fld = header.field_structures[i]; if (fld.type == StarlingDbfField.FieldType.character && fld.fieldlength == 6) { /* * We are dealing with a pointer to the VAR file here. * The first 4 bytes give the position, the last 2 bytes give length. */ foreach (StarlingDbfRecord rec in records) { byte[] old_content = rec.field_contents[i]; if (!StarlingDbfRecord.emptyVarRef(old_content)) { uint position = BitConverter.ToUInt32(old_content, 0); ushort length = BitConverter.ToUInt16(old_content, 4); varFile.BaseStream.Seek(position, SeekOrigin.Begin); byte[] new_content = varFile.ReadBytes(length); rec.field_contents[i] = new_content; } } } reportProgress(60 + (40d * i) / header.field_structures.Count, "Processing records"); } } } } reportProgress(100, "Database loaded"); // log records //foreach (StarlingDbfRecord rec in records) // Trace.WriteLine(rec.ToString()); } else { Trace.WriteLine("DBF file not found."); } // unregister event if (progressHandler != null) { onProgressChanged -= progressHandler; } }