为了使用C语言编程直接读取dbf文件,需要了解dbf文件的二进制文件格式,下面给出简要的说明。 |
表文件由头记录及数据记录组成。头记录定义该表的结构并包含与表相关的其他信息。头记录由文件位置 0 开始。数据记录1紧接在头记录之后(连续的字节),包含字段中实际的文本。 |
记录的长度(以字节为单位)等于所有字段定义的长度之和。表文件中存储整数时低位字节在前。 |
1.表头记录的结构: |
字节偏移 说明 |
0 文件类型 |
0x02FoxBASE |
0x03FoxBASE+/dBASE III PLUS,无备注 |
0x30Visual FoxPro |
0x43dBASE IV SQL 表文件,无备注 |
0x63dBASE IV SQL 系统文件,无备注 |
0x83FoxBASE+/dBASE III PLUS,有备注 |
0x8BdBASE IV 有备注 |
0xCBdBASE IV SQL 表文件,有备注 |
0xF5FoxPro 2.x(或更早版本)有备注 |
0xFBFoxBASE |
1 - 3 最近一次更新的时间(YYMMDD) |
4 - 7 文件中的记录数目 |
8 - 9 第一个数据记录的位置 |
10 - 11 每个数据记录的长度(包括删除标记) |
12 - 27 保留 |
28 表的标记 |
0x01具有 .cdx 结构的文件 |
0x02文件包含备注。 |
0x04文件是数据库(.dbc) |
请注意,这个字节可以包含任何上面值的和。例如,0x03 表明表具有结构化.cdx和一个备注字段。 |
29 代码页标记 |
30 - 31 保留,包含 0x00 |
32 - n 字段子记录 |
字段的数目决定了字段子记录的数目。表中每个字段都对应一个字段子记录。 |
n+1 头记录终止符(0x0D),n+2 到 n+264 此范围内的 263 个字节包含后链信息(相关数据库 (.dbc) 的相对路径)。如果第一个字节为 0x00,则该文件不与数据库关联。因此数据库文件本身总是包含 0x00。 |
1 头记录中的第 8 到第 9 个字节指示数据文件中数据的起始位置。数据记录从 除标记字节开始。如果此字节为 ASCII 空格 (0x20),该记录没有删除标记, 如果第一字节为星号 (0x2A),该记录有删除标记。在标记之后是字段记录中所命名各字段中的数据 |
2.字段子记录结构 |
字节偏移 说明 |
0 - 10 字段名(最多 10 个字符 -若少于 10 则用空字符 (0x00) 填充) |
11 字段类型 |
C-字符型 |
Y-货币型 |
N-数值型 |
F-浮点型 |
D-日期型 |
T-日期时间型 |
B-双精度型 |
I-整型 |
L-逻辑型 |
M-备注型 |
G-通用型 |
C-字符型(二进制) |
M-备注型(二进制) |
P-图片型 |
12 - 15 记录中该字段的偏移量 |
16 字段长度(以字节为单位) |
17 小数位数 |
18 字段标记 |
0x01系统列(用户不可见) |
0x02可存储 null 值的列 |
0x04二进制列(只适于字符型和备注型) |
19 - 32 保留 |
格式保存的文件标头: |
支持 null 值 |
日期时间型、货币型及双精度型数据 |
字符字段和备注字段标记为二进制 |
在数据库 (.dbc) 文件中添加表 |
提示 可以使用下面的公式求出表文件中字段的数目:(x - 296/32) 公式中,x 表示第一个记录的位置(表头记录的第 8 到第 9 个字节),296 表示 263(后链信息)+ 1(头记录终止符)+ 32(第一个字段子记录),32 是字段子记录的长度。 |
因为dbf文件的记录在文件数据部分,都是用ASCII码形式存放的,所以只要读出文件头和字段类型描述区的内容,就可以直接读取dbf文件中的每条记录,dbf文件头结构和字段类型描述结构用C语言表示如下: |
struct dbf_head { /* DBF文件头结构 */ |
char vers; /* 版本标志*/ |
unsigned char yy,mm,dd; /* 最后更新年、月、日 */ |
unsigned long no_recs; /* 文件包含的总记录数 */ |
unsigned short head_len,rec_len; /* 文件头长度,记录长度 */ |
char reserved[20]; /* 保留 */ |
}; |
struct field_element{ /* 字段描述结构 */ |
char field_name[11]; /* 字段名称 */ |
char field_type; /* 字段类型 */ |
unsigned long offset; /* 偏移量 */ |
unsigned char field_length; /* 字段长度 */ |
unsigned char field_decimal; /* 浮点数整数部分长度 */ |
char reserved1[2]; /* 保留 */ |
char dbaseiv_id; /* dBASE IV work area id */ |
char reserved2[10]; /* |
char production_index; |
}; |
需要注意的是,输入的dbf文件是FoxPro 2.5 for DOS/Windows的版本,文件头中表示记录数等内容的unsigned long和unsigned short字段,寻址顺序是从高位到低位;而C程序在HP-UX操作系统下编译时,HP服务器使用的RISC CPU的寻址顺序与Intel x86系列CPU的寻址顺序相反,是从低位到高位,故程序中需要将读取的unsigned long和unsigned short进行反转操作,可以使用位操作编程实现: |
void revert_unsigned_short(unsigned short *a) |
{ |
unsigned short left,right; |
left=right=*a; |
*a=((left&0x00ff)<<8)|((right&0xff00)>>8); |
} |
void revert_unsigned_long(unsigned long *a) |
{ |
unsigned long first,second,third,forth; |
first=second=third=forth=*a; |
*a=((first&0x000000ff)<<24)| |
((second&0x0000ff00)<<8)| |
((third&0x00ff0000)>>8)| |
((forth&0xff000000)>>24); |
} |
根据上面对dbf文件格式的分析,使用Pro*C编程,即可以很方便地实现直接读取dbf文件的功能。具体源代码附录如下: |
#include |
#include |
#include |
#include |
#include |
#include |
#include |
#include |
#include |
#include |
#include |
#include |
#define NFIELDS 5 |
#define TRUE 1 |
#define FALSE 0 |
/* DBF文件头结构 */ |
struct dbf_head { |
char vers; /* version identifier */ |
unsigned char yy,mm,dd; /* yymmdd */ |
unsigned int no_recs; /* records number */ |
unsigned short head_len,rec_len; /* head_length,record_length */ |
char reserved[20]; /* reserved bytes */ |
}; |
/* DBF字段描述结构 */ |
struct field_element{ |
char field_name[11]; |
char field_type; |
unsigned int offset; |
unsigned char field_length; |
unsigned char field_decimal; |
char reserved1[2]; |
char dbaseiv_id; |
char reserved2[10]; |
char production_index; |
}; |
char *dbf_fields_name[NFIELDS]={ |
"a", "b","c","d","e" |
}; |
/* 全局变量 */ |
struct dbf_head file_head; |
struct field_element *fields; |
int *length; |
unsigned int *offset; |
/*函数原形声明*/ |
void revert_unsigned_short(unsigned short *a); |
void revert_unsigned_int(unsigned int *a); |
void revert_unsigned_short(unsigned short *a) |
{ |
unsigned short left,right; |
left=right=*a; |
*a=((left&0x00ff)<<8)|((right&0xff00)>>8); |
} |
void revert_unsigned_int(unsigned int *a) |
{ |
unsigned int first,second,third,forth; |
first=second=third=forth=*a; |
*a=((first&0x000000ff)<<24)| |
((second&0x0000ff00)<<8)| |
((third&0x00ff0000)>>8)| |
((forth&0xff000000)>>24); |
} |
int main(int argc,char *argv[]) |
{ |
register int i,j; |
FILE *fp_dat; |
char *buffer; |
char *allspace; |
int fields_count,matched=FALSE; |
unsigned int counts; |
if((fp_dat=fopen("a.dbf","rb"))==NULL){ |
fprintf(stderr,"Cannot open dbf file to read!/n"); |
exit(1); |
} |
fseek(fp_dat,0L,SEEK_SET); |
fread((void*)&file_head,sizeof(struct dbf_head),1,fp_dat); |
revert_unsigned_int(&file_head.no_recs); |
revert_unsigned_short(&file_head.head_len); |
revert_unsigned_short(&file_head.rec_len); |
fields_count=(file_head.head_len-sizeof(struct dbf_head)-1)/sizeof(struct field_element); |
if((fields=(struct field_element*)malloc(sizeof(struct field_element)*fields_count))==NULL){ |
fprintf(stderr,"Cannot allocate memory for fields array !/n"); |
fclose(fp_dat); |
exit(2); |
} |
if((buffer=(char*)malloc(sizeof(char)*file_head.rec_len))==NULL){ |
fprintf(stderr,"Cannot allocate memory for record buffer!/n"); |
fclose(fp_dat); |
exit(3); |
} |
if((allspace=(char*)malloc(sizeof(char)*file_head.rec_len))==NULL){ |
fprintf(stderr,"Cannot allocate memory for all_space record buffer!/n"); |
fclose(fp_dat); |
exit(4); |
} |
else{ |
memset((void*)allspace,'/x20',file_head.rec_len-1); |
allspace[file_head.rec_len]='/0'; |
} |
fread((void*)fields,sizeof(struct field_element),fields_count,fp_dat); |
for(i=0;i < td> |
revert_unsigned_int(&fields[i].offset); |
fields[0].offset=1; |
for(i=1;i < td> |
fields[i].offset=fields[i-1].offset+(unsigned short)fields[i-1].field_length; |
length=(int*)malloc(sizeof(int)*fields_count); |
offset=(unsigned int*)malloc(sizeof(unsigned int)*fields_count); |
if(length==NULL||offset==NULL){ |
fprintf(stderr,"Cannot allocate memory for array length or offset./n"); |
exit(-1); |
} |
for(i=0;i < td> |
{ |
for(j=0;j < td> |
{ |
if(strcmp(dbf_fields_name[i],fields[j].field_name)==0) |
{ |
offset[i]=fields[j].offset - 1; |
length[i]=fields[j].field_length; |
matched=TRUE; |
break; |
} |
if(!matched){ |
fprintf(stderr,"dbf file structure is invalid, field %s not found./n", |
dbf_fields_name[i]); |
exit(-1); |
} |
else |
matched=FALSE; |
} |
} |
fseek(fp_dat,(long)file_head.head_len,SEEK_SET); |
for(counts=0;counts < td> |
{ |
if(fgetc(fp_dat)==(int)'/x2a') |
{ |
fseek(fp_dat,(int)file_head.rec_len-1,SEEK_CUR); |
continue; |
} |
fread((void*)buffer,(int)file_head.rec_len-1,1,fp_dat); |
buffer[file_head.rec_len]='/0'; |
if(strcmp(buffer,allspace)==0) /*去掉全为空格的记录行 |
continue; |
/* 进一步处理buffer中数据 */ |
} |
fclose(fp_dat); |
free(buffer); |
free(allspace); |
free(offset); |
free(length); |
return 0; |
} |