.dbf文件格式
.dbf文件格式描述可以看这两篇博客:
关于dbf文件格式笔者不再赘述,因为上述两篇博客已经讲的很明白了。这篇文章主要是要讲怎么通过C++来读取任意.dbf文件。
C++代码
1.Field类
.dbf是表文件,以二进制方式存储,头文件是变长的。
既然是表文件,那么就存在行列的概念。DBF表的行表示为记录,列表示为字段(field)。因此,可以设计一个字段类,即Class Field。
代码如下:
/********************************************************************************
* Description: this header file is designed for reading and saving
* the field in the dBaseFile
*
* Author: Mr.Zhang Wanglin(Geocat)
*
* Date: 2020.06.07
********************************************************************************/
#ifndef FIELD_H
#define FIELD_H
#include <vector>
using std::vector;
class Field
{
public:
Field();
void storeFieldContent();
enum _eRecordItemDataType{B,C,D,G,L,M,N}; // 记录项的数据类型
// 属性—— 1. 文件头中字段的内容:32字节
// 0-10字节为记录项(字段)名称
char _cTitle[11];
// 11字节为记录项的数据类型
char _cDataType;
// 16字节为记录项长度,BYTE类型,1个字节
// 注:可以用强制类型转换将记录项长度转换成int型
unsigned char _ucFieldLength;
// 字段内容
char _cFieldContent[100];
vector<char*> _vField; // 存储字段的内容
};
#endif // FIELD_H
2.DBaseFile类
DBaseFIle类包含文件头里的内容,以及所有字段的内容。
头文件代码如下:
/********************************************************************************
* Description: this header file is designed for reading and saving
* the field in the dBaseFile
*
* Author: Mr.Zhang Wanglin(Geocat)
*
* Date: 2020.06.08
********************************************************************************/
#ifndef DBASEFILE_H
#define DBASEFILE_H
#include <string>
#include <vector>
#include <fstream>
#include "field.h"
using namespace std;
class DBaseFile
{
public:
// 构造函数
DBaseFile();
DBaseFile(string sFilename);
// 自定义函数
// loadFile(string)函数将文件读取到内存
void loadFile(string sFilename);
void showData();
// 数据表的增删改查
void addRec(int iColum, int iLine, unsigned char* ucData);
void deleteRec(int iColum, int iLine, unsigned char* ucData);
void modifyRec(int iColum, int iLine, unsigned char* ucData);
void checkRec(int iColum, int iLine, unsigned char* ucData);
// 属性
// 文件头中的内容
int _iRecCount; // 记录的条数——行数
int _iFieldCount; // 字段数——列数
short _BytesOfFileHead; // 文件头中的字节数
short _BytesOfEachRecord; // 每一条记录的字节数
// 内存中用来存储相应数据的变量
Field* _pField; // 用来创建某个字段
string _sFilename; // 用来存储文件名
// vector<char*> _vFieldNameInFileHead; // 文件头中的字段名
vector<Field*> _vTable; // 用来存储所有的字段的内容,是一个存储字段容器
protected:
void readFileHead(ifstream& inFile);
void readFileRecord(ifstream& inFile);
bool isReadFileOK(string sFilename);
};
#endif // DBASEFILE_H
3. 具体实现
field.cpp
#include "field.h"
Field::Field()
{
}
void Field:: storeFieldContent()
{
_vField.push_back(_cFieldContent);
}
dbasefile.cpp
#include "dbasefile.h"
#include <iostream>
DBaseFile::DBaseFile()
{}
DBaseFile::DBaseFile(string sFilename)
{
this->_sFilename=sFilename;
}
void DBaseFile:: loadFile(string sFilename)
{
ifstream inFile(sFilename,ios::binary|ios::in);
if(!isReadFileOK(sFilename))
return;
readFileHead(inFile);
readFileRecord(inFile);
inFile.close();
}
void DBaseFile:: showData()
{
for(unsigned int i=0;i<_vTable.size();i++)
{
cout<<_vTable[i]->_cTitle<<"\t";
}
cout<<"\n";
for(int i=0;i<_iRecCount;i++)
{
for(unsigned int j=0;j<_vTable.size();j++)
cout<<_vTable[j]->_vField[i]<<"\t";
cout<<endl;
}
}
bool DBaseFile::isReadFileOK(string sFilename)
{
ifstream inFile(sFilename,ios::binary|ios::in);
if(inFile.good())
{
inFile.close();
return true;
}
else
return false;
}
void DBaseFile:: readFileHead(ifstream& inFile)
{
if(!isReadFileOK(_sFilename))
return;
// 读取文件头中的记录条数,即行数
inFile.seekg(4,ios::beg);
inFile.read((char*)&_iRecCount,sizeof (int));
// 读取文件头的字节数
inFile.read((char*)&_BytesOfFileHead,sizeof (short));
// 读取一条记录中的字节长度
inFile.read((char*)&_BytesOfEachRecord,sizeof (short));
// 计算字段数,即列数
_iFieldCount = (_BytesOfFileHead-33)/32;
// 开始读取文件头中关于字段的描述
for(int i=0;i<_iFieldCount;i++)
{
inFile.seekg(32+32*i,ios::beg);
_pField=new Field;
for(int j=0;j<11;j++) // 读取字段名,存入_cTitle数组
inFile.read(_pField->_cTitle+j,sizeof (char));
inFile.read(&_pField->_cDataType,sizeof (char)); // 读取字段的数据类型并存入_cDataType
inFile.seekg(4,ios::cur);
inFile.read((char*)&_pField->_ucFieldLength,sizeof (char)); // 读取字段长度,此时的字段长度为BYTE类型,需通过强制类型转换成int型
_vTable.push_back(_pField);
}
}
void DBaseFile:: readFileRecord(ifstream& inFile)
{
inFile.seekg(_BytesOfFileHead,ios::beg);
char cDeleteTag;
for(int i=0;i<_iRecCount;i++)
{
inFile.read(&cDeleteTag,sizeof (char));
for(unsigned int j=0;j<_vTable.size();j++)
{
char*cRecord=new char[(int)_vTable[j]->_ucFieldLength];
for(int k=0;k<(int)_vTable[j]->_ucFieldLength;k++)
inFile.read(cRecord+k,sizeof (char));
_vTable[j]->_vField.push_back(cRecord);
}
}
}
void DBaseFile:: addRec(int iColum, int iLine, unsigned char* ucData)
{}
void DBaseFile:: deleteRec(int iColum, int iLine, unsigned char* ucData)
{}
void DBaseFile:: modifyRec(int iColum, int iLine, unsigned char* ucData)
{}
void DBaseFile:: checkRec(int iColum, int iLine, unsigned char* ucData)
{}
总结
本文从DBF文件格式出发,抽象Field类和DBaseFile类,代码具有一定的通用性,对于一般情况下的DBF文件,本文代码都能读取。但是本文仅仅只是将DBF文件读到内存,尚未涉及文件的写操作,因此具有一定的局限性。但对于DBF文件格式,本文代码可以帮助读者理解。