C++老代码 -- DBF数据文件操作类DBFile

本文介绍了一个用于读写DBF文件的C++类DBFile,适用于早期的dBASE、FoxBase和FoxPro数据库文件操作。该类支持基本的文件读写、记录追加等功能,并提供了字段管理和数据类型转换的支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

十几年前,dBASE、FoxBase和FoxPro数据库盛极一时,C/C++程序员使用C/C++直接操作DBF数据文件是理所当然的事,下面是我在1994年写的一个DBFile类代码。

DBFIle类的头文件:

// DBFIO.HPP

#ifndef__DBFIO_HPP
#define __DBFIO_HPP

#include
< stdlib.h >
#include
< fstream.h >
#include
" marray.hpp "

const int

DB_FieNameSize
= 11 ;

class DBField // DBF文件的字段类
{

public :

char name[DB_FieNameSize];
char type;
int off;
int nul;
unsigned
char width;
unsigned
char dec;
DBField()
{
memset(
this , 0 , sizeof (DBField));
}
int operator == ( const DBField & );
int operator < ( const DBField & );
DBField
& operator = ( const DBField & );
// 设置字段.参数:名称,类型,宽度,小数位
void SetValue( char * , int , int , int = 0 );
};

inline
int DBField:: operator == ( const DBField & d)
{
return ! strcmp(name,d.name);
}

inline
int DBField:: operator < ( const DBField & d)
{
return strcmp(name,d.name) < 0 ? 1 : 0 ;
}

inlineDBField
& DBField:: operator = ( const DBField & d)
{
memmove(
this , & d, sizeof (DBField));
return * this ;
}

// 定义排序字段数组类

typedefMSArray
< DBField > DBFieldArray;

class DBFile: public fstream // DBF文件类
{

typedef
struct
{
unsigned
char dbfflag;
unsigned
char date_n;
unsigned
char date_y;
unsigned
char date_r;
long records;
unsignedldb,lrd;
char _nul[ 20 ];
}DBFSTRUCT;

DBFSTRUCTstr;
char * buf;
DBFieldTmpField;
int fields;
long oldrecords;
int openerror;
DBFieldArraydArray;
void SetBuf();
void Init();
void SetFields( int );
void WriteDelFlag( long , int = ' * ' );

public :

DBFile();
// 调用Use(char*)
DBFile( const char * );
// 调用Use(char*,DBField*,int)
DBFile( const char * ,DBField * , int );
~ DBFile();
// 打开一个已存在文件;参数:文件名
void Use( const char * );
// 用一字段数组建立新文件;参数:文件名,字段数组,字段数
void Use( const char * ,DBField * , int );
// 关闭文件
void Use();
void Close();
// 返回记录数
long Records();
// 返回记录长度
int RecSize();
// 返回文件头结构长度
int TopSize();
// 返回字段数
int Fields();
// 返回打开文件时的错误代码,错误码:
// 0无错误
// 1文件不存在
// 2建立新文件失败
// 3建立文件时未设置字段
// 4读文件头出错或非DBF文件
// 5写文件头出错
// 6内存不够
int OpenError();
// 把当前记录内容读到缓冲区
void Read();
// 将缓冲区内容写到当前记录
void Write();
// 取一字段内容到字符串中;参数:字段名,字符串
char * Get( char * , char * );
// 取一字段内容到字符串中;参数:字段序号(按字段名排过序),字符串
char * Get(unsigned, char * );
// 将字符串内容输出到字段中;参数:字段名,字符串
void Put( char * , const char * );
// 将字符串内容输出到字段中;参数:字段序号(按字段名排过序),字符串
void Put(unsigned, const char * );
// 将一浮点数输出到字段中;参数:,字段名,浮点数
void Put( char * , double );
// 将一浮点数输出到字段中;参数:字段序号(按字段名排过序),浮点数
void Put(unsigned, double );
// 将缓冲区内容追加到文件尾;参数:追加标记(0空记录)
void Append( int = 0 );
// 将一字段内容转换为浮点数返回(未检查字段类型);参数:字段名
double operator []( char * );
// 功能同上;参数:字段序号(按字段名排过序)
double operator [](unsigned);
// 移动文件记录put指针;参数:记录号
void Seekp( long );
// 移动文件记录get指针;参数:记录号
void Seekg( long );
// 将缓冲区内容输出到文件;参数:记录号
DBFile & operator << ( long );
// 从文件中输入内容到缓冲区中;参数:记录号
DBFile & operator >> ( long );
// 返回缓冲区指针
char * Buf();
// 在字段排序数组中查找字段,返回序号,未找到返回0X7FFF;参数:字段名
unsignedFindField( char * );
// 返回字段排序数组
DBFieldArray & FieldArray();
// 给记录打上删除标记
void Delete( long );
// 取消记录的删除标记
void UnDelete( long );
// 如记录号在文件记录范围内返回TRUE,否则返回FALSE
int InRecords( long );

};

inlineDBFile::DBFile():dArray(
0 , 1 )
{
Init();
}

inline
long DBFile::Records()
{
return str.records;
}

inline
int DBFile::RecSize()
{
return str.lrd;
}

inline
int DBFile::TopSize()
{
return str.ldb;
}

inline
int DBFile::Fields()
{
return fields;
}

inline
char * DBFile::Buf()
{
return buf;
}

inlineDBFieldArray
& DBFile::FieldArray()
{
return dArray;
}

inline
void DBFile::Use()
{
Close();
}

inlineDBFile::
~ DBFile()
{
Close();
}

inline
int DBFile::OpenError()
{
return openerror;
}

inlineunsignedDBFile::FindField(
char * name)
{
strcpy(TmpField.name,strupr(name));
return dArray.Find(TmpField);
}

inline
void DBFile::Read()
{
read(buf,str.lrd);
}

inline
void DBFile::Write()
{
* buf = 32 ;
write(buf,str.lrd);
}

inline
char * DBFile::Get( char * name, char * s)
{
return Get(FindField(name),s);
}

inline
void DBFile::Put( char * name, const char * s)
{
Put(FindField(name),s);
}

inline
void DBFile::Put( char * name, double s)
{
Put(FindField(name),s);
}

inline
double DBFile:: operator []( char * name)
{
return atof(Get(name,str._nul));
}

inline
double DBFile:: operator [](unsignedi)
{
return atof(Get(i,str._nul));
}

inline
void DBFile::Seekp( long recnum)
{
seekp(recnum
* str.lrd + str.ldb);
}

inline
void DBFile::Seekg( long recnum)
{
seekg(recnum
* str.lrd + str.ldb);
}

inline
void DBFile::Delete( long recnum)
{
WriteDelFlag(recnum);
}

inline
void DBFile::UnDelete( long recnum)
{
WriteDelFlag(recnum,
32 );
}

inline
int DBFile::InRecords( long recnum)
{
return (recnum >= 0 && recnum < str.records);
}

#endif

DEFile类的CPP文件:

// DBFILE.CPP

#include
" dbfio.hpp "
#include
< ctype.h >

void DBField::SetValue( char * n, int t, int w, int d)
{
strcpy(name,strupr(n));
type
= toupper(t);
width
= w;
dec
= d;
}

void DBFile::Init()
{
fields
= 0 ;
buf
= 0 ;
}

void DBFile::SetFields( int n)
{
fields
= n;
if (n)
{
dArray.SetLimit(n);
buf
= new char [str.lrd + 1 ];
buf[
0 ] = 32 ;
buf[str.lrd]
= 0x1a ;
if ( ! buf || ! dArray.Items())
openerror
= 6 ;
}
}

void DBFile::Close()
{
if (oldrecords != str.records)
{
seekp(
4 );
write((
char * ) & str.records, sizeof (unsigned long ));
oldrecords
= str.records;
}
close();
if (buf)
delete[]buf;
dArray.RemoveAll();
Init();
if (openerror)
setstate(ios::badbit);
}

char * DBFile::Get(unsignedi, char * s)
{
if (i < dArray.Count())
{
memcpy(s,
& buf[dArray[i].off],dArray[i].width);
s[dArray[i].width]
= 0 ;
}
else
* s = 0 ;
return s;
}

DBFile
& DBFile:: operator >> ( long recnum)
{
if (InRecords(recnum))
{
Seekg(recnum);
Read();
}
else
memset(buf,
32 ,str.lrd);
return * this ;
}

void DBFile::WriteDelFlag( long recnum, int Flag)
{
Seekp(recnum);
write((
char * ) & Flag, 1 );
}

// DBFPUT.CPP

#include
" dbfio.hpp "
#include
< strstrea.h >
#include
< iomanip.h >
#include
< bcd.h >

void DBFile::Put(unsignedi, const char * s)
{
if (i < dArray.Count())
{
int flag = ios::left;
ostrstreamos(buf,str.lrd);
os.seekp(dArray[i].off);
if (dArray[i].type == ' N ' || dArray[i].type == ' F ' )
flag
= ios::right;
os
<< setw(dArray[i].width) << setiosflags(flag) << s;
}
}

void DBFile::Put(unsignedi, double val)
{
if (i < dArray.Count())
{
ostrstreamos(buf,str.lrd);
bcda(val,dArray[i].dec);
os.seekp(dArray[i].off);
os
<< setw(dArray[i].width) << setiosflags(ios::right | ios:: fixed ) << a;
}
}

void DBFile::Append( int flag)
{
if ( ! flag)
memset(buf,
32 ,str.lrd);
Seekp(str.records);
Write();
str.records
++ ;
}

DBFile
& DBFile:: operator << ( long recnum)
{
if (InRecords(recnum))
{
Seekp(recnum);
Write();
}
else
Append(
1 );
return * this ;
}

// DBFUSE.CPP

#include
" dbfio.hpp "

DBFile::DBFile(
const char * name):dArray( 0 , 1 )
{
Init();
Use(name);
}

void DBFile::Use( const char * name)
{
if (fields)
Close();
open(name,ios::
in | ios:: out | ios::binary | ios::nocreate);
if (bad())
{
openerror
= 1 ;
return ;
}
openerror
= 0 ;
read((
char * ) & str, sizeof (DBFSTRUCT));
oldrecords
= str.records;
if (fail() || str.dbfflag != 3 )
openerror
= 4 ;
else
SetFields(str.ldb
/ 32 - 1 );
if ( ! openerror)
{
DBFieldField;
for ( int i = 0 ;i < fields;i ++ )
{
read((
char * ) & Field, sizeof (DBField));
dArray.Add(Field);
seekg(
32 - sizeof (DBField),ios::cur);
}
seekg(
1 ,ios::cur);
if (fail())
openerror
= 4 ;
}
if (openerror)
Close();
}

DBFile::DBFile(
const char * name,DBField * fie, int n):dArray( 0 , 1 )
{
Init();
Use(name,fie,n);
}

void DBFile::Use( const char * name,DBField * fie, int n)
{
if ( ! fie || ! n)
{
openerror
= 3 ;
return ;
}
if (fields)
Close();
openerror
= 0 ;
str.lrd
= 1 ;
for ( int i = 0 ;i < n;str.lrd += fie[i].width,i ++ )
fie[i].off
= str.lrd;
str.dbfflag
= 3 ;
str.date_n
= 96 ;
str.date_y
= str.date_r = 1 ;
str.ldb
= n * 32 + 33 ;
memset(str._nul,
0 , 20 );
open(name,ios::
in | ios:: out | ios::binary | ios::trunc);
if (bad())
openerror
= 2 ;
else
{
str.records
= oldrecords = 0 ;
write((
char * ) & str, sizeof (DBFSTRUCT));
SetFields(n);
if ( ! openerror)
{
for ( int i = 0 ;i < fields;i ++ )
{
write((
char * ) & fie[i], sizeof (DBField));
write(str._nul,
32 - sizeof (DBField));
dArray.Add(fie[i]);
}
i
= 0x0d ;
write((
char * ) & i, 1 );
if (fail())
openerror
= 5 ;
}
}
if (openerror)
Close();
}
DBFIle类所使用的动态数组模板类文件:
// MARRAY.HPP

#ifndef__MARRAY_HPP
#define __MARRAY_HPP

#include
< string .h >
#include
< iostream.h >

// 无序直接数组类模板.用户类中应定义默认构造函数和运算符==.

template
< class T > class MArray
{

protected :

char * items; // 动态数组指针
int count; // 数组中对象个数
int limit; // 数组容量
int delta; // 数组增量
int typesize;
MArray(){}
void Init( int , int , int );
virtual T & Item( int index)
{
return * (T * ) & items[index * typesize];
}
virtual void Let( int index, const T * t)
{
memmove(
& items[index * typesize],t,typesize);
}

public :

// 构造函数.参数:数组容量(个);增加量
MArray( int , int = 0 );
~ MArray();
void SetLimit( int );
// 移去一个对象,其后对象前移.参数:数组下标
void Remove( int );
// 移去指定位置及其后的所有对象;参数:数组下标
void RemoveAll( int = 0 );
// 增加对象到指定位置,其后对象后移,返回实际下标,出错返回0x7fff.
// 参数:数组下标;对象
int AddAt( int , const T & );
// 增加对象到数组尾部,返回实际的数组下标,出错返回0x7fff.参数:对象
int Add( const T & );
// 返回动态数组指针
char * Items();
// 查找对象,返回对象下标,未找到返回0X7FFF.参数:对象
int Find( const T & );
// 返回数组中的对象个数
int Count();
// 返回数组容量大小
int ArraySize();
// 返回对象的内存长度
int ObjectSize();
// 返回数组下标所指的对象,下标超范围时返回值不定
T & operator []( int );

};

template
< class T > inlineMArray < T > ::MArray( int mLimit, int mDelta)
{
Init(mLimit,mDelta,
sizeof (T));
}

template
< class T > inline void MArray < T > ::RemoveAll( int index)
{
if (index >= 0 && index < count)
count
= index;
}

template
< class T > inline char * MArray < T > ::Items()
{
return items;
}

template
< class T > inline int MArray < T > ::Add( const T & t)
{
return AddAt(count,t);
}

template
< class T > inline int MArray < T > ::Count()
{
return count;
}

template
< class T > inline int MArray < T > ::ArraySize()
{
return limit;
}

template
< class T > inline int MArray < T > ::ObjectSize()
{
return typesize;
}

template
< class T > inlineT & MArray < T > :: operator []( int index)
{
return Item(index);
}

#pragma option-Jgx

template
< class T >
void MArray < T > ::Init( int mLimit, int mDelta, int size)
{
items
= 0 ;
count
= 0 ;
limit
= 0 ;
delta
= mDelta;
typesize
= size;
SetLimit(mLimit);
}

template
< class T > MArray < T > :: ~ MArray()
{
if (items)
delete[]items;
}

template
< class T > void MArray < T > ::SetLimit( int aLimit)
{
if (aLimit <= limit)
return ;
if (aLimit > 0xfff0 / typesize)
aLimit
= 0xfff0 / typesize;
char * aItems = new char [aLimit * typesize];
if (aItems)
{
if (count)
memmove(aItems,items,count
* typesize);
if (items)
delete[]items;
items
= aItems;
limit
= aLimit;
}
}

template
< class T > void MArray < T > ::Remove( int index)
{
if (index < 0 || index >= count)
return ;
count
-- ;
if (count)
memmove(
& items[index * typesize],
& items[(index + 1 ) * typesize],(count - index) * typesize);
}

template
< class T > int MArray < T > ::AddAt( int index, const T & t)
{
if (index < 0 )
return 0x7fff ;
if (count == limit)
SetLimit(count
+ delta);
if (count == limit)
return 0x7fff ;
if (index < count)
memmove(
& items[(index + 1 ) * typesize],
& items[index * typesize],
(count
- index) * typesize
);
else
index
= count;
Let(index,
& t);
count
++ ;
return index;
}

template
< class T > int MArray < T > ::Find( const T & t)
{
for ( int i = 0 ;i < count;i ++ )
if (Item(i) == t)
return i;
return 0x7fff ;
}

#pragma option-Jg

// 有序直接数组类模板.用户类中应定义默认构造函数和运算符==和<.

template
< class T > class MSArray: public virtual MArray < T >
{

protected :

int index; // 搜索时保存的数组下标值
int res; // 搜索成功标记.0未找到匹配对象
int duplicates; // 允许重复插入标记
MSArray(){}
int Search( const T & key);

public :

// 构造函数.参数:数组容量(个);增量;允许重复插入标记(0不允许重复)
MSArray( int l, int d = 0 , int u = 0 );
// 将对象t按索引方式插到数组中,返回下标.如t属重复对象,
// 重复插入标记!=0,t被插入到相同对象的尾部,否则返回0x7fff.
int Add( const T & t);
// 用二分法查找第一个匹配对象,返回数组下标,无匹配对象返回0x7fff.
int Find( const T & t);
// 在Find()基础上,查找下一匹配对象,返回数组下标,无匹配对象返回0x7fff.
int FindNext();
// 返回使用Find()或FindNext()后>=关键对象的下标值
int IndexNum()
{
return index;
}
};

#pragma option-Jgx

template
< class T > int MSArray < T > ::FindNext()
{
if (res && duplicates && ++ index < count
&& Item(index - 1 ) == Item(index))
return index;
res
= 0 ;
return 0x7fff ;
}

template
< class T > int MSArray < T > ::Search( const T & key)
{
int l = 0 ,i;
int h = count - 1 ;
res
= 0 ;
while (l <= h)
{
i
= (l + h) >> 1 ;
if (Item(i) < key)
l
= i + 1 ;
else
{
h
= i - 1 ;
if (Item(i) == key)
{
res
= 1 ;
if ( ! duplicates)
l
= i;
}
}
}
index
= l;
return res;
}

template
< class T > int MSArray < T > ::Add( const T & t)
{
int i = Find(t);
if (i == 0x7fff )
i
= index;
else
{
if ( ! duplicates)
return 0x7fff ;
for (i ++ ;Item(i) == t && i < count;i ++ );
}
return AddAt(i,t);
}

template
< class T > int MSArray < T > ::Find( const T & t)
{
if (Search(t))
return index;
return 0x7fff ;
}

template
< class T > MSArray < T > ::MSArray( int l, int d, int u):
MArray
< T > (l,d),
duplicates(u),
res(
0 )
{}

#pragma option-Jg

// 无序间接数组类模板.退出时不删除对象本身,其余同MArray<T>类

template
< class T > class IMArray: public virtual MArray < T >
{

protected :

IMArray(){}
virtual void Let( int i, const T * t)
{
unsigned
long p = (unsigned long )t;
memmove(
& items[i * typesize], & p,typesize);
}
virtual T & Item( int i)
{
return * (T * )( * ( long * ) & items[i * typesize]);
}

public :

IMArray(
int Limit, int Delta = 0 )
{
Init(Limit,Delta,
sizeof (T * ));
}

};

// 有序间接数组类模板.退出时不删除对象本身.其余同MSArray<T>类

template
< class T > class IMSArray: public MSArray < T > , public IMArray < T >
{

protected :

IMSArray(){}

public :

IMSArray(
int Limit, int Delta = 0 , int u = 0 ):
IMArray
< T > (Limit,Delta)
{
duplicates
= u;
res
= 0 ;
}

};

#endif

对以上代码作些简单的说明:

1、DBFile类代码使用的Borland C++ 3.1编译器,其它C++编译器可能要作些修改。

2、DBFile中打开和关闭文件使用的是Use函数,没有使用Open、Create这些熟悉的函数名,是按照dBASE命令习惯命名的。

3、DEFile类没有提供DBF文件排序方法,是因为我还有个通用的 B- 树排序类,实际操作中,需要排序时,2个类结合使用的,那个类有个纯虚方法,需要继承才能使用,因为没有找到以前排序类测试代码例子,所以我正在考虑是否把那个排序类贴上来,如果贴上来,还得写一个例子,而我不用C++的时间太长,机器上又没有安装BC3.1,不知能否写好这个例子。

4、DBFile类使用了动态数组,而当时是没有STL的(即使有,我可能也不会使用,因为DOS下的资源相当紧张,而且BC3.1自身也带有数组类,和现在的STL类似,但是我嫌使用它麻烦),所以自己写了个模板类,该模板类在其它C++版本编译必须做编译选项的修改。

声明,文章的代码是1995年前的东西,只能供初学者们借鉴参考。有错误或建议,请来信:maozefa@hotmail.com

更新:今天找了个以前DBFile类的测试代码,更新在此。另外补充说明一点:这个DBFile类用今天的眼光看,是比较简陋的,其中的方法也较低阶,一般数据文件操作方法如First、Last、Next、Bof、Eof等都没有。原因是当时使用C/C++的人,一般都不大喜欢dBASE,但是为了能读取当时大量的dbf文件的数据,写一个能读dbf文件,转换成自己的文件格式的代码就不错了,我这个类在当时已经是很完善了,不仅能读写,还能创建dbf文件。通过以前这个例子,不难看出,我们当时需要的只是文件转换,或者数字字段计算功能(对数字字段使用了[]重载,可以直接以字段名或字段序号操作)[2007-9-17 13:43P]。

#include < iostream.h >
#include
" DBFio.hpp "

void CreateDbf( char * fileName)
{
DBFieldfields[
3 ];
fields[
0 ].SetValue( " Code " , ' C ' , 4 );
fields[
1 ].SetValue( " Name " , ' C ' , 8 );
fields[
2 ].SetValue( " Value " , ' N ' , 10 , 2 );
DBFileDbf;
Dbf.Use(fileName,fields,
3 );
Dbf.Put(
" Code " , " 0001 " );
Dbf.Put(
" Name " , " Maozefa " );
Dbf.Put(
" Value " , 12345.67 );
Dbf.Append(
1 );
Dbf.Put(
" Code " , " 0002 " );
Dbf.Put(
" Name " , " Maojun " );
Dbf.Put(
" Value " , 78543.21 );
Dbf.Append(
1 );
Dbf.Use();
}

void main()
{
CreateDbf(
" Test.dbf " );
DBFiledbf(
" Test.dbf " );
char code[ 5 ],name[ 9 ];
for ( long i = 0 ;i < dbf.Records();i ++ )
{
dbf.Read();
dbf.Get(
" Code " ,code);
dbf.Get(
" Name " ,name);
cout
<< code << ' ' << name << ' ' << dbf[ " Value " ] << endl;
}
system(
" pause " );
}

声明,文章的代码是1995年前的东西,只能供初学者们借鉴参考。有错误或建议,请来信:maozefa@hotmail.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值