shp系列(六)——利用C++进行Dbf文件的写(创建)

14 篇文章 3 订阅
11 篇文章 0 订阅

上一篇介绍了shp文件的创建,接下来介绍dbf的创建。

推荐结合读取dbf的博客一起看!

推荐结合读取dbf的博客一起看!

推荐结合读取dbf的博客一起看!

 

1.Dbf头文件的创建

Dbf头文件的结构如下:

记录项数组说明:

字段类型说明:

关于每项的具体含义参照读取dbf文件的解释,这里重点解释几项:

  • HeaderByteNum指dbf头文件的字节数,数值不用除于2,具体为:从version到Reserved2(共32) + n个字段 * 每一个字段长度 32 + terminator。
  • RecordByteNum指每条记录的字节数,RecordByteNum根据记录的实际长度来写数值不用除于2,具体为:∑每个字段的字节数(字段数量根据读取打开shp的字段数决定)。例如我的例子中写了八个字段,则一条记录的实际长度为:1(deleteFlag) + 10 + 32 + 16 + 10 + 10 + 8 + 19 + 19 = 1 + 124 =125。

 

2.Dbf记录实体的创建

记录实体就是每条记录,一个记录有多个字段,部分字段上存储必要的信息。由于实际上每个shp文件的表的字段数可能不一样,并且每个字段的类型不固定,需要每次判定字段类型,然后根据不同类型设置来输出信息。

但是这费时费力,根据实际情况,简化一下,读取已知字段数和字段类型的DBF的信息,或者说,根据实际需要的字段数和字段类型来输出,牺牲普遍性来获取快速结果,以后修改也不困难。

 

3.读取Dbf的代码

void WriteDbf(CString filename)
{
	//创建与Shp文件同名的指针
	int n = filename.ReverseFind('.');
	filename = filename.Left(n);
	filename = filename + ".dbf";
	FILE* m_DbfFile_fp;
	if ((m_DbfFile_fp = fopen(filename, "wb")) == NULL)
		return;
	
	//****创建dbf文件的文件头
	int i, j;
	BYTE version = 4;
	fwrite(&version, 1, 1, m_DbfFile_fp);
	CTime t = CTime::GetCurrentTime();
	int d = t.GetDay();
	int y = t.GetYear() % 2000;
	int m = t.GetMonth();
	BYTE date[3];
	date[0] = y;
	date[1] = m;
	date[2] = d;
	for (i = 0; i<3; i++)                           //记录时间
		fwrite(date + i, 1, 1, m_DbfFile_fp);

	int RecordNum = map->layer->objects.size();     //文件中的记录条数
	fwrite(&RecordNum, sizeof(int), 1, m_DbfFile_fp);
	short HeaderByteNum = 0;                        //文件头中的字节数,暂时写0,后面要返回来修改
	fwrite(&HeaderByteNum, sizeof(short), 1, m_DbfFile_fp);
	short RecordByteNum = 0;                        //一条记录中的字节长度,暂时写0,后面要返回来修改
	fwrite(&RecordByteNum, sizeof(short), 1, m_DbfFile_fp);
	short Reserved1 = 0;
	fwrite(&Reserved1, sizeof(short), 1, m_DbfFile_fp);
	BYTE Flag4s = 0;
	fwrite(&Flag4s, sizeof(BYTE), 1, m_DbfFile_fp);
	BYTE EncrypteFlag = 0;
	fwrite(&EncrypteFlag, sizeof(BYTE), 1, m_DbfFile_fp);
	int Unused[3] = { 0,0,0 };
	for (i = 0; i<3; i++)
		fwrite(Unused + i, sizeof(int), 1, m_DbfFile_fp);
	BYTE MDXFlag = 0;
	fwrite(&MDXFlag, sizeof(BYTE), 1, m_DbfFile_fp);
	BYTE LDriID = 0;
	fwrite(&LDriID, sizeof(BYTE), 1, m_DbfFile_fp);
	short Reserved2 = 0;
	fwrite(&Reserved2, sizeof(short), 1, m_DbfFile_fp);

	//****写记录项数组
	int fieldscount = fieldscount_final;          //字段数量可以根据读取的shp文件确定
	for (i = 0; i< fieldscount; i++)
	{
		RecordItem recordItem = recordItems[i];   //recordItems是自己设置的记录项数组(字段)的数组,
												  //根据需求设定每个记录项数组(字段)的参数,以供调用
		//****name--------11     bytes
		fwrite(recordItem.name, 11, 1, m_DbfFile_fp);

		//****FieldType----1     bytes
		fwrite(&(recordItem.fieldType), sizeof(BYTE), 1, m_DbfFile_fp);
		
		//****Reserved3----4     bytes
		fwrite(&(recordItem.Reserved3), sizeof(int), 1, m_DbfFile_fp);

		//****FieldLength--1     bytes
		fwrite(&(recordItem.fieldLength), sizeof(BYTE), 1, m_DbfFile_fp);
	
		//****DecimalCount-1   bytes
		fwrite(&(recordItem.decimalCount), sizeof(BYTE), 1, m_DbfFile_fp);

		//****Reserved4----2     bytes
		fwrite(&(recordItem.Reserved4), sizeof(short), 1, m_DbfFile_fp);

		//****WorkID-------1    bytes
		fwrite(&(recordItem.workID), sizeof(BYTE), 1, m_DbfFile_fp);

		//****Reserved5----10   bytes
		for (j = 0; j<5; j++)
			fwrite(recordItem.Reserved5 + j, sizeof(short), 1, m_DbfFile_fp);

		//****MDXFlag1-----1  bytes
		fwrite(&(recordItem.mDXFlag1), sizeof(BYTE), 1, m_DbfFile_fp);
	}
	BYTE terminator = 13;                       //头文件终止标识符
	fwrite(&terminator, sizeof(BYTE), 1, m_DbfFile_fp);

	fseek(m_DbfFile_fp, 8, SEEK_SET);           //转到头文件字节数RecordByteNum,开始重写
	HeaderByteNum = 32 + 32 * fieldscount + 1;  //从version到Reserved2(共32) + n个字段 * 每一个字段长度 32 + terminator
	fwrite(&HeaderByteNum, sizeof(short), 1, m_DbfFile_fp);

	RecordByteNum = 1 + 124;                    //RecordByteNum根据记录的实际长度来写,∑每个字段的长度 
												// 1 + 10 + 32 + 16 + 10 + 10 + 8 + 19 + 19 = 1 + 124 =125
	fseek(m_DbfFile_fp, 10, SEEK_SET);          //转移每条记录长度RecordByteNum
	fwrite(&RecordByteNum, sizeof(short), 1, m_DbfFile_fp);
	fseek(m_DbfFile_fp, 0, SEEK_END);
	//****写dbf文件头结束

	//****写每条记录
	BYTE deleteFlag;
	char media[40];
	for (i = 1; i <= RecordNum; i++){
		CGeoPolygon* polygon = (CGeoPolygon*)map->layer->objects[i - 1];
		deleteFlag = 32;                                    //默认写32
		fwrite(&deleteFlag, sizeof(BYTE), 1, m_DbfFile_fp); //读取删除标记  1字节

		//****写 ObjectID int
		stringstream ss;
		ss << (i - 1);
		string str = ss.str();
		int length = str.length();
		memset(media, '\0', 40);
		for (int m = 0; m < 10 - length; m++)
			media[m] = ' ';

		for (int c = 10 - length; c < 10; c++)
			media[c] = str[c - 10 + length];

		for (j = 0; j<10; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--10					

		//****写Dest string
		memset(media, '\0', 40);
		media[0] = '/';
		for (int c = 1; c <32; c++)
			media[c] = ' ';
	
		for (j = 0; j<32; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--32

		//****写Ec string
		for (j = 0; j<16; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--16

		//****写EcRm int
		ss << -8888;
		str = ss.str();
		length = str.length();
		memset(media, '\0', 40);
		for (int m = 0; m < 10 - length; m++) 
			media[m] = ' ';
		
		for (int c = 10 - length; c < 10; c++) 
			media[c] = str[c - 10 + length];
		
		for (j = 0; j<10; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--10

		//****写Elevt int
		for (j = 0; j<10; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--10

		//****写Cc int
		str = polygon->objectAttribute;
		memset(media, '\0', 40);
		length = str.length();
		for (int c = 0; c < length; c++) 
			media[c] = str[c];
		
		for (int c = length; c < 8; c++) 
			media[c] = ' ';
		
		for (j = 0; j<8; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--8

		//****写shape_length double
		CString str1;
		double shape_length = polygon->getAllLength();
		str1.Format(_T("%.11e"), shape_length);
		memset(media, '\0', 40);
		media[0] = ' ';
		for (int c = 1; c < 16; c++) 
			media[c] = str1[c - 1];
		
		if (str1.GetLength() == 18)
			for (int c = 16; c < 19; c++) 
				media[c] = str1[c - 1];
		else {
			media[16] = '0';
			media[17] = str1[15];
			media[18] = str1[16];
		}
		//*(media + length ) = '\0';
		for (j = 0; j<19; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--19

		//****写shape_Area double
		double shape_area = polygon->shapeArea;
		str1.Format(_T("%.11e"), shape_area);
		memset(media, '\0', 40);
		media[0] = ' ';
		for (int c = 1; c < 16; c++) 
			media[c] = str1[c - 1];
		
		if (str1.GetLength() == 18)
			for (int c = 16; c < 19; c++) 
				media[c] = str1[c - 1];
		else {                  
			media[16] = '0';
			media[17] = str1[15];
			media[18] = str1[16];
		}
		for (j = 0; j<19; j++)
			fwrite(media + j, sizeof(char), 1, m_DbfFile_fp);   //--19
	}
	//****写dbf文件记录结束

	fclose(m_DbfFile_fp);
}

下一篇将介绍Shx的创建。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个用C++shp、shx、dbf的基本框架及示例代码: ```cpp #include <iostream> #include <fstream> #include <string> #include <vector> using namespace std; // SHP文件头 #pragma pack(push, 1) struct SHPHeader { int32_t fileCode; int32_t unused[5]; int32_t fileLength; int32_t version; int32_t shapeType; double bbox[4]; double zRange[2]; double mRange[2]; }; #pragma pack(pop) // SHX文件头 #pragma pack(push, 1) struct SHXHeader { int32_t fileCode; int32_t unused[5]; int32_t fileLength; int32_t version; int32_t shapeType; double bbox[4]; double zRange[2]; double mRange[2]; }; #pragma pack(pop) // DBF文件头 #pragma pack(push, 1) struct DBFHeader { uint8_t version; uint8_t lastUpdate[3]; uint32_t numRecords; uint16_t headerLength; uint16_t recordLength; uint8_t unused[20]; }; #pragma pack(pop) // DBF字段类型 enum class DBFFieldType : uint8_t { Character = 'C', Numeric = 'N', Float = 'F', Date = 'D', Logical = 'L', Memo = 'M', General = 'G' }; // DBF字段描述 #pragma pack(push, 1) struct DBFFieldDesc { uint8_t name[11]; DBFFieldType type; uint32_t dataAddress; uint8_t length; uint8_t decimalCount; uint8_t unused[14]; }; #pragma pack(pop) // DBF记录 #pragma pack(push, 1) struct DBFRecord { uint8_t deletedFlag; uint8_t data[1]; // 实际数据长度为字段长度之和 }; #pragma pack(pop) // SHP文件 void writeSHP(const string& fileName, const vector<Point>& points) { // 创建文件 ofstream outfile(fileName, ios::binary); if (!outfile) { cout << "Failed to create " << fileName << endl; return; } // 文件SHPHeader header = { 9994, { 0, 0, 0, 0, 0 }, 0, 1000, 1, { 0, 0, 0, 0 }, { 0, 0 }, { 0, 0 } }; int32_t numShapes = 1; int32_t contentLength = numShapes * (4 + 8 + 4 + 4 + points.size() * 16 + 4); header.fileLength = contentLength + sizeof(SHPHeader); outfile.write(reinterpret_cast<const char*>(&header), sizeof(header)); // 实体记录 int32_t shapeType = 1; // 点 double bbox[4] = { DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX }; for (const auto& p : points) { if (p.x < bbox[0]) bbox[0] = p.x; if (p.y < bbox[1]) bbox[1] = p.y; if (p.x > bbox[2]) bbox[2] = p.x; if (p.y > bbox[3]) bbox[3] = p.y; } outfile.write(reinterpret_cast<const char*>(&shapeType), sizeof(shapeType)); outfile.write(reinterpret_cast<const char*>(bbox), sizeof(bbox)); int32_t numParts = 0; int32_t numPoints = points.size(); outfile.write(reinterpret_cast<const char*>(&numParts), sizeof(numParts)); outfile.write(reinterpret_cast<const char*>(&numPoints), sizeof(numPoints)); for (const auto& p : points) { outfile.write(reinterpret_cast<const char*>(&p.x), sizeof(p.x)); outfile.write(reinterpret_cast<const char*>(&p.y), sizeof(p.y)); } int32_t endMarker = -1; // 终止标记 outfile.write(reinterpret_cast<const char*>(&endMarker), sizeof(endMarker)); // 关闭文件 outfile.close(); } // SHX文件 void writeSHX(const string& fileName, const vector<Point>& points) { // 创建文件 ofstream outfile(fileName, ios::binary); if (!outfile) { cout << "Failed to create " << fileName << endl; return; } // 文件头 SHXHeader header = { 9994, { 0, 0, 0, 0, 0 }, 0, 1000, 1, { 0, 0, 0, 0 }, { 0, 0 }, { 0, 0 } }; int32_t numShapes = 1; int32_t contentLength = numShapes * 8; header.fileLength = contentLength + sizeof(SHXHeader); outfile.write(reinterpret_cast<const char*>(&header), sizeof(header)); // 实体记录 int32_t offset = sizeof(SHPHeader) + 8; // 实体记录的偏移量 int32_t contentLength2 = 4 + 8 + 4 + 4 + points.size() * 16 + 4; outfile.write(reinterpret_cast<const char*>(&offset), sizeof(offset)); outfile.write(reinterpret_cast<const char*>(&contentLength2), sizeof(contentLength2)); // 终止标记 int32_t endMarker = -1; // 终止标记 outfile.write(reinterpret_cast<const char*>(&endMarker), sizeof(endMarker)); // 关闭文件 outfile.close(); } // DBF文件 void writeDBF(const string& fileName, const vector<Point>& points) { // 创建文件 ofstream outfile(fileName, ios::binary); if (!outfile) { cout << "Failed to create " << fileName << endl; return; } // 文件DBFHeader header = { 3, { 0, 0, 0 }, static_cast<uint32_t>(points.size()), 33, 16, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; int fieldCount = 2; int fieldLength = 16; header.headerLength = 33 + fieldCount * 32; header.recordLength = fieldLength * fieldCount + 1; // +1是为了删除标记 outfile.write(reinterpret_cast<const char*>(&header), sizeof(header)); // 字段描述 DBFFieldDesc fields[] = { { "ID", DBFFieldType::Numeric, 0, 8, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, { "Name", DBFFieldType::Character, 8, 8, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } }; outfile.write(reinterpret_cast<const char*>(fields), sizeof(fields)); // 记录 for (size_t i = 0; i < points.size(); i++) { DBFRecord record = { 0 }; int id = i + 1; string name = "Point " + to_string(id); memcpy(&record.data[0], &id, sizeof(id)); memcpy(&record.data[8], name.c_str(), name.length()); outfile.write(reinterpret_cast<const char*>(&record), header.recordLength); } // 关闭文件 outfile.close(); } int main() { // 创建点集 vector<Point> points = { {1, 2}, {3, 4}, {5, 6}, {7, 8} }; // SHP文件 writeSHP("points.shp", points); // SHX文件 writeSHX("points.shx", points); // DBF文件 writeDBF("points.dbf", points); return 0; } ``` 上述代码中,我们定义了三个结构体分别表示SHP文件头、SHX文件头和DBF文件头,以及一些常量和枚举类型。在SHP和SHX文件时,我们按照文件格式规定,分别文件头和实体记录。在DBF文件时,我们同样按照文件格式规定,文件头、字段描述和记录。 上述代码只是一个示例,实际应用中需要根据不同的数据结构和需求进行相应的修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值