1.DBF文件要点
DBF文件又叫属性文件,也叫dBASE文件,文件后缀是.dbf,实际上ArcGIS打开后的属性表就是DBF的信息。DBF文件遵循以下几个条件:
- 每个要素在表中必须要包含一个与之相对应的记录。
- 记录的顺序必需与要素在主文件中(*.shp)的顺序一样。
- dBASE 文件头中的年份值必须要晚于 1900 年。
2.DBF文件的组成
属性文件(.dbf)用于记录属性信息。它是一个标准的DBF文件,也是由头文件和实体信息两部分构成:
3.DBF文件的头文件
文件头部分的长度是不定长的,它主要对DBF文件作了一些总体说明。
其中最主要的是对这个DBF文件的记录项(字段)的信息进行了详细地描述,比如对每个记录项(字段)的名称、数据类型、长度等信息都有具体的说明。
3.1头文件
- date[3],BYTE,更新日期
- verision,BYTE类型,版本信息
- RecordNum,int,文件中记录条数
- HeaderByteNum,short,文件头的字节数
- RecordByteNum,short,一条记录的字节长度
- Reserved1,short
- Flag4s,BYTE
- EncrypteFlag,BYTE
- Unused[3],int,保留字节
- MDXFlag,BYTE,MDX标识
- LDriID,BYTE
- Reserved2,short
- RecordItem(记录项数组详情见下),32,字段描述信息
- terminator,BYTE,终止标识
- 头文件的字节数为:1 + 1 * 3 + 4 + 2 + 2 + 2 + 1 + 1 + 4 * 3 + 1 + 1 + 2 + 32 * RecordNum + 1 = 33 + 32 * RecordNum
3.2记录项数组
记录项数组其实就是描述表中字段信息的数组
- name[11],BYTE,字段名
- fieldType,BYTE,字段类型,包括B、C、D、G、L、M和N
- Reserved3,int
- fieldLength,BYTE,记录项长度
- decimalCount,BYTE,记录项精度
- Reserved4,short
- workID,BYTE
- Reserved5[5],short,
- mDXFflag1,BYTE
- 一个记录项字节数为:11 + 1 + 4 + 1 + 1 + 2 + 1 + 5 * 2 + 1 = 32
记录项描述信息中fieldType的类型说明
4.实体信息
实体信息部分就是一条条属性记录,每条记录都是由若干个记录项(字段)构成,因此只要依次循环读取每条记录就可以了。
5.读取DBF代码
由于实际上每个shp文件的表的字段数可能不一样,并且每个字段的类型不固定,需要每次判定字段类型,然后根据不同类型设置来读取信息。可以根据字段数量设置一个数组,数组的每个元素存储对应顺序字段的类型,然后根据数组元素的值定义变量获取记录的信息。
上述想法是一种比较完善的做法,即对于任何数量和类型的字段都可以满足要求,也应该是ArcGIS读取表的方法。完善即代表需要考虑各种情况,费时费力。
这里根据实际情况,简化一下,读取已知字段数和字段类型的DBF的信息。
假设要读取一个八个字段的表:
- int类型:ObjectID,Ecrm,Elevt
- double类型:shapeArea,shapeLength
- CString类型:Dest,Ec,cc
代码如下
void readDbf(CString filename) { //****在读取shp之后打开DBF文件 int n = filename.ReverseFind('.'); filename = filename.Left(n); filename = filename + ".dbf"; FILE* m_DbfFile_fp;//****Dbf文件指针 if ((m_DbfFile_fp = fopen(filename, "rb")) == NULL)//打开dbf文件 return; //****读取dbf文件的文件头 int i, j; BYTE version; fread(&version, 1, 1, m_DbfFile_fp); BYTE date[3]; for (i = 0; i<3; i++) fread(date + i, 1, 1, m_DbfFile_fp); int RecordNum;//文件中的记录条数 fread(&RecordNum, sizeof(int), 1, m_DbfFile_fp); short HeaderByteNum;//文件头中的字节数 fread(&HeaderByteNum, sizeof(short), 1, m_DbfFile_fp); short RecordByteNum;//一条记录中的字节长度 fread(&RecordByteNum, sizeof(short), 1, m_DbfFile_fp); short Reserved1; fread(&Reserved1, sizeof(short), 1, m_DbfFile_fp); BYTE Flag4s; fread(&Flag4s, sizeof(BYTE), 1, m_DbfFile_fp); BYTE EncrypteFlag; fread(&EncrypteFlag, sizeof(BYTE), 1, m_DbfFile_fp); int Unused[3]; for (i = 0; i<3; i++) fread(Unused + i, sizeof(int), 1, m_DbfFile_fp); int a = Unused[0]; int b = Unused[1]; int c = Unused[2]; BYTE MDXFlag; fread(&MDXFlag, sizeof(BYTE), 1, m_DbfFile_fp); BYTE LDriID; fread(&LDriID, sizeof(BYTE), 1, m_DbfFile_fp); short Reserved2; fread(&Reserved2, sizeof(short), 1, m_DbfFile_fp); BYTE name[11]; BYTE fieldType; int Reserved3; BYTE fieldLength; BYTE decimalCount; short Reserved4; BYTE workID; short Reserved5[5]; BYTE mDXFlag1; int fieldscount; fieldscount = (HeaderByteNum - 32) / 32; fieldscount_final = fieldscount; //****读取记录项信息-共有8个记录项 for (i = 0; i< fieldscount; i++)//字段数 { RecordItem recordItem; //定义记录项存储信息,便于写dbf使用 fread(name, 11, 1, m_DbfFile_fp); //FieldName----11 bytes memcpy(recordItem.name, name, 11); fread(&fieldType, sizeof(BYTE), 1, m_DbfFile_fp); //FieldType----1 bytes recordItem.fieldType = fieldType; fread(&Reserved3, sizeof(int), 1, m_DbfFile_fp); //Reserved3----4 bytes recordItem.Reserved3 = Reserved3; fread(&fieldLength, sizeof(BYTE), 1, m_DbfFile_fp); //FieldLength--1 bytes recordItem.fieldLength = fieldLength; fread(&decimalCount, sizeof(BYTE), 1, m_DbfFile_fp);//DecimalCount-1 bytes recordItem.decimalCount = decimalCount; fread(&Reserved4, sizeof(short), 1, m_DbfFile_fp); //Reserved4----2 bytes recordItem.Reserved4 = Reserved4; fread(&workID, sizeof(BYTE), 1, m_DbfFile_fp); //WorkID-------1 bytes recordItem.workID = workID; for (j = 0; j<5; j++) //Reserved5----10 bytes fread(Reserved5 + j, sizeof(short), 1, m_DbfFile_fp); memcpy(recordItem.Reserved5, Reserved5, 10); fread(&mDXFlag1, sizeof(BYTE), 1, m_DbfFile_fp); //MDXFlag1-----1 bytes recordItem.mDXFlag1 = mDXFlag1; recordItems.push_back(recordItem); } BYTE terminator; //terminator----1 bytes fread(&terminator, sizeof(BYTE), 1, m_DbfFile_fp); //****读取dbf文件头结束 //****读取dbf文件记录 开始 int ObjectID, Ecrm, Elevt; double shapeArea, shapeLength; CString Dest, Ec, cc; BYTE deleteFlag; char media[40]; vector<CString> polygonAttribute; vector<double> ShapeArea; vector<int>temp1; for (i = 0; i<RecordNum; i++) { fread(&deleteFlag, sizeof(BYTE), 1, m_DbfFile_fp); //读取删除标记 1字节 //****读取 ObjectID int for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<10; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--10 ObjectID = atoi(media); //****读取 Dest string for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<32; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--32 Dest = media; //****读取 Ec string for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<16; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--16 Ec = media;//同上 //****读取 EcRm int for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<10; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--10 Ecrm = atoi(media); //****读取 Elevt int for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<10; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--10 Elevt = atoi(media); //****读取 Cc int for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<8; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--8 cc = media;//4值4'' //****读取 shape_length double for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<19; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--19 shapeLength = atof(media);//带e的 //****读取 shape_Area double for (j = 0; j<40; j++) strcpy(media + j, "\0"); for (j = 0; j<19; j++) fread(media + j, sizeof(char), 1, m_DbfFile_fp); //--19 shapeArea = atof(media); } //****读取dbf文件记录 结束 }
这是在已知字段数和字段类型情况下读取的情况,如果未知的情况下,需要设置数组存储每个字段的类型,则数组的长度就是字段数量;在读取实体记录时,根据数组的每个元素的值设定对应类型的变量,来存储记录信息,必要时最后结果要转化(如字符型转化成整型)。这一部分大家可以自己去完善。
下一篇我们将讲述shx文件的读取。