30
DBase::DBase(): m_recordCount( 0 )
30
DBase::DBase(): m_recordCount(0)
36
while(!fields.isEmpty()) delete fields.takeFirst();
36
while (!fields.isEmpty()) delete fields.takeFirst();
40
// Headerdefinition in dBASE
44
// unsigned char version 0 dBASE-Version (3)
45
// unsigned char last_update[3] 1-3 Date of last update
46
// unsigned long records 4-7 Number of records
47
// unsigned short header_length 8-9 headerlength
48
// unsigned short record_length 10-11 recordlength
49
// unsigned char reserved[20] 12-31 reserverd info from dBase
40
// Headerdefinition in dBASE
44
// unsigned char version 0 dBASE-Version (3)
45
// unsigned char last_update[3] 1-3 Date of last update
46
// unsigned long records 4-7 Number of records
47
// unsigned short header_length 8-9 headerlength
48
// unsigned short record_length 10-11 recordlength
49
// unsigned char reserved[20] 12-31 reserverd info from dBase
52
bool DBase::load( const QString& filename )
52
bool DBase::load(const QString& filename)
55
m_file.setFileName( filename );
56
if( !m_file.open(QIODevice::ReadOnly) )
59
m_stream.setDevice( &m_file );
60
m_stream.setByteOrder( QDataStream::LittleEndian );
62
unsigned filesize = m_file.size();
67
m_version = ver & 0x7f; // bit 7: has memo ?
69
// only dBASE V.3 is supported
73
// date of last update
75
m_stream >> y >> m >> d;
76
// because dBASE saves 102 instead of 2002 (very Y2K-save ;-)
77
m_lastUpdate.setYMD( y+1900, m, d );
79
// check for valid date
80
if( !m_lastUpdate.isValid() ) return false;
85
m_recordCount = norec;
88
quint16 header_length;
89
m_stream >> header_length;
90
m_headerLength = header_length;
93
quint16 record_length;
94
m_stream >> record_length;
95
m_recordLength = record_length;
97
// read the remaining chars
99
for (int foo = 0; foo < 20; ++foo)
102
// size of file must match
103
if( filesize < m_headerLength + m_recordLength * m_recordCount )
106
// Now read the headers of the columns and their type
110
// unsigned char field_name[11] 0-10 Fieldname
111
// unsigned char field_type 11 Fieldtype
112
// unsigned long field_address 12-15 Fielddataaddress
113
// unsigned char field_length 16 Fieldlength
114
// unsigned char field_decimals 17 decimals
115
// unsigned char reserved[14] 18-31 reserved for internal dBASE-stuff
117
while(!fields.isEmpty()) delete fields.takeFirst();
118
for( unsigned i = 1; i < m_headerLength/32; ++i )
120
DBaseField* field = new DBaseField;
124
for ( int j = 0; j < 11; ++j)
125
m_stream >> colname[j];
127
field->name = QString( (const char*) &colname[0] );
134
case 'C': field->type = DBaseField::Character; break;
135
case 'N': field->type = DBaseField::Numeric; break;
136
case 'D': field->type = DBaseField::Date; break;
137
case 'M': field->type = DBaseField::Memo; break;
138
case 'L': field->type = DBaseField::Logical; break;
139
default: field->type = DBaseField::Unknown; break;
149
field->length = colsize;
153
m_stream >> decimals;
154
field->decimals = decimals;
156
// read remaining chars
55
m_file.setFileName(filename);
56
if (!m_file.open(QIODevice::ReadOnly))
59
m_stream.setDevice(&m_file);
60
m_stream.setByteOrder(QDataStream::LittleEndian);
62
unsigned filesize = m_file.size();
67
m_version = ver & 0x7f; // bit 7: has memo ?
69
// only dBASE V.3 is supported
73
// date of last update
75
m_stream >> y >> m >> d;
76
// because dBASE saves 102 instead of 2002 (very Y2K-save ;-)
77
m_lastUpdate.setYMD(y + 1900, m, d);
79
// check for valid date
80
if (!m_lastUpdate.isValid()) return false;
85
m_recordCount = norec;
88
quint16 header_length;
89
m_stream >> header_length;
90
m_headerLength = header_length;
93
quint16 record_length;
94
m_stream >> record_length;
95
m_recordLength = record_length;
97
// read the remaining chars
158
for ( int foo = 0; foo < 14; ++foo )
162
fields.append( field );
165
// set the index to the first record
166
m_stream.device()->seek( m_headerLength );
99
for (int foo = 0; foo < 20; ++foo)
102
// size of file must match
103
if (filesize < m_headerLength + m_recordLength * m_recordCount)
106
// Now read the headers of the columns and their type
110
// unsigned char field_name[11] 0-10 Fieldname
111
// unsigned char field_type 11 Fieldtype
112
// unsigned long field_address 12-15 Fielddataaddress
113
// unsigned char field_length 16 Fieldlength
114
// unsigned char field_decimals 17 decimals
115
// unsigned char reserved[14] 18-31 reserved for internal dBASE-stuff
117
while (!fields.isEmpty()) delete fields.takeFirst();
118
for (unsigned i = 1; i < m_headerLength / 32; ++i) {
119
DBaseField* field = new DBaseField;
123
for (int j = 0; j < 11; ++j)
124
m_stream >> colname[j];
126
field->name = QString((const char*) & colname[0]);
132
case 'C': field->type = DBaseField::Character; break;
133
case 'N': field->type = DBaseField::Numeric; break;
134
case 'D': field->type = DBaseField::Date; break;
135
case 'M': field->type = DBaseField::Memo; break;
136
case 'L': field->type = DBaseField::Logical; break;
137
default: field->type = DBaseField::Unknown; break;
147
field->length = colsize;
151
m_stream >> decimals;
152
field->decimals = decimals;
154
// read remaining chars
156
for (int foo = 0; foo < 14; ++foo)
160
fields.append(field);
163
// set the index to the first record
164
m_stream.device()->seek(m_headerLength);
171
QStringList DBase::readRecord( unsigned recno )
169
QStringList DBase::readRecord(unsigned recno)
175
// out of range ? return empty strings
176
if( recno >= m_recordCount )
178
for( unsigned i=0; i<fields.count(); i++)
173
// out of range ? return empty strings
174
if (recno >= m_recordCount) {
175
for (int i = 0; i < fields.count(); i++)
180
// seek to where the record is
181
qint64 filepos = m_headerLength + recno * m_recordLength;
182
m_stream.device()->seek(filepos);
184
// first char == '*' means the record is deleted
185
// so we just skip it
187
m_stream >> delmarker;
188
if (delmarker == 0x2a)
192
for (int i = 0; i < fields.count(); i++)
193
switch (fields.at(i)->type) {
194
// Numeric or Character
195
case DBaseField::Numeric:
196
case DBaseField::Character: {
199
for (unsigned j = 0; j < fields.at(i)->length; j++) {
200
m_stream >> ch; str += QChar(ch);
207
case DBaseField::Logical: {
211
case 'Y': case 'y': case 'T': case 't': result.append("True"); break;
212
case 'N': case 'n': case 'F': case 'f': result.append("False"); break;
213
default: result.append(""); break;
218
// Date, stored as YYYYMMDD
219
// Note: convert it to YYYY-MM-DD
220
case DBaseField::Date: {
223
for (unsigned j = 0; j < fields.at(i)->length; j++) {
224
m_stream >> ch; str += QChar(ch);
232
// Unknown/Unimplemented
233
case DBaseField::Unknown:
234
case DBaseField::Memo:
236
result.append(""); // unknown
183
// seek to where the record is
184
qint64 filepos = m_headerLength + recno * m_recordLength;
185
m_stream.device()->seek( filepos );
187
// first char == '*' means the record is deleted
188
// so we just skip it
190
m_stream >> delmarker;
191
if( delmarker == 0x2a )
195
for( unsigned i=0; i<fields.count(); i++ )
196
switch( fields.at(i)->type )
198
// Numeric or Character
199
case DBaseField::Numeric:
200
case DBaseField::Character:
204
for( unsigned j=0; j<fields.at(i)->length; j++ )
205
{ m_stream >> ch; str += QChar(ch); }
206
result.append( str );
210
case DBaseField::Logical:
216
case 'Y': case 'y': case 'T': case 't': result.append( "True" ); break;
217
case 'N': case 'n': case 'F': case 'f': result.append( "False" ); break;
218
default: result.append( "" ); break;
222
// Date, stored as YYYYMMDD
223
// Note: convert it to YYYY-MM-DD
224
case DBaseField::Date:
228
for( unsigned j=0; j<fields.at(i)->length; j++ )
229
{ m_stream >> ch; str += QChar(ch); }
230
str.insert( 6, '-' );
231
str.insert( 4, '-' );
232
result.append( str );
235
// Unknown/Unimplemented
236
case DBaseField::Unknown:
237
case DBaseField::Memo:
239
result.append( "" ); // unknown
246
243
void DBase::close()
248
if( m_file.isOpen() ) m_file.close();
245
if (m_file.isOpen()) m_file.close();