关闭

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

标签: c++duplicatesclassiosdeletesearch
7938人阅读 评论(4) 收藏 举报
分类:

        十几年前,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( 
this0sizeof( DBField ) );
  }
  
int operator == ( const DBField& );
  
int operator < ( const DBField& );
  DBField
& operator = ( const DBField& );
  
// 设置字段.参数: 名称, 类型, 宽度, 小数位
  void SetValue( char*intintint = 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;
}

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

// 定义排序字段数组类

typedef MSArray 
< 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;
    unsigned ldb, lrd;
    
char _nul[20];
  }DBFSTRUCT;

  DBFSTRUCT str;
  
char *buf;
  DBField TmpField;
  
int fields;
  
long oldrecords;
  
int openerror;
  DBFieldArray dArray;
  
void SetBuf();
  
void Init();
  
void SetFields( int );
  
void WriteDelFlag( longint = '*' );

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;参数: 字段名
  unsigned FindField( char* );
  
// 返回字段排序数组
  DBFieldArray& FieldArray();
  
// 给记录打上删除标记
  void Delete( long );
  
// 取消记录的删除标记
  void UnDelete( long );
  
// 如记录号在文件记录范围内返回 TRUE, 否则返回 FALSE
  int InRecords( long );

};

inline DBFile::DBFile() : dArray( 
01 )
{
  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;
}

inline DBFieldArray
& DBFile::FieldArray()
{
  
return dArray;
}

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

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

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

inline unsigned DBFile::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 [] ( unsigned i )
{
  
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( unsigned i, char *s )
{
  
if( i < dArray.Count() )
  {
    memcpy( s, 
&buf[dArray[i].off], dArray[i].width );
    s[ dArray[i].width ] 
= 0;
  }
  
else
    
*= 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( unsigned i, const char *s )
{
  
if( i < dArray.Count() )
  {
    
int flag = ios::left;
    ostrstream os( 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( unsigned i, double val )
{
  
if( i < dArray.Count() )
  {
    ostrstream os( buf, str.lrd );
    bcd a( 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( 01 )
{
  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 )
  {
    DBField Field;
    
forint 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( 01 )
{
  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;
  
forint 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, 
020 );
  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 )
    {
      
forint 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( intintint );
  
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( intint = 0 );
  
~MArray();
  
void SetLimit( int );
  
// 移去一个对象,其后对象前移.参数: 数组下标
  void Remove( int );
  
// 移去指定位置及其后的所有对象; 参数:数组下标
  void RemoveAll( int = 0 );
  
// 增加对象到指定位置,其后对象后移,返回实际下标,出错返回 0x7fff.
  
// 参数: 数组下标;对象
  int AddAt( intconst 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> inline MArray <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> inline T& 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 )
{
  
forint 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 = 0int 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 = 0int 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)
{
    DBField fields[
3];
    fields[
0].SetValue("Code"'C'4);
    fields[
1].SetValue("Name"'C'8);
    fields[
2].SetValue("Value"'N'102);
    DBFile Dbf;
    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");
    DBFile dbf(
"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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:881118次
    • 积分:12285
    • 等级:
    • 排名:第1411名
    • 原创:134篇
    • 转载:0篇
    • 译文:0篇
    • 评论:1078条
    最新评论