1. 程式人生 > >shp系列(三)——利用C++進行DBF檔案的讀(開啟)

shp系列(三)——利用C++進行DBF檔案的讀(開啟)

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檔案的讀取。