使用C++ 标准库中的中的Map对自定义的结构体数据集进行排序+去重复

Visual C++实战开发:从MFC框架到现代桌面应用构建 在Windows平台开发领域,Visual C++作为原生开发的核心工具链,其重要性不言而喻。其核心原理基于微软基础类库(MFC)框架,通过消息驱动机制和文档/视图架构,实现了高效的桌面应用程序开发。这一技术价值在于能够构建高性能、高稳定性的系统级应用,广泛应用于工业控制、企业管理系统、传统桌面软件等场景。在开发实践中,开发者常需处理如“microsoft visual c++ redistributable”运行时依赖问题,并利用Visual Studio强大的调试工具进行问题排查。本文聚焦于如何通过实例 阅读详情

今天写了一个三维网格保存ply格式的小工具函数,因为ply类型是顶点按索引排列的方式,不同于STL类型的按面片索引的方式。因此,准备借助于C++中的Map类型,来进行顶点的去重复和排序。因为对Map结构的不了解,竟然花了一天的时间去实现,中间走了不少歪路,也借鉴了不少其他的方法,最终实现了自己想要的结果。纸上得来终觉浅,绝知此事要躬行,很有必要记录一下,以此为戒。

首先定义STL类型的读取结构:ReadSTL.h

#pragma once

#include<d3d9.h>
#include<windows.h>
#include<vector>
using namespace std;



//-----------------------------顶点结构体----------------------------------

struct VertexT //顶点结构
{
	VertexT() {}
	VertexT(float x, float y, float z)
	{
		_x = x;  _y = y;  _z = z;
	}
	float _x, _y, _z;
	static const DWORD FVF;
};

class Point3f
{
public:
	Point3f() :x(0), y(0), z(0)
	{
	};
	Point3f(float _x, float _y, float _z) :x(_x), y(_y), z(_z){};
	int SetParam(float _x, float _y, float _z)
	{
		x = _x;
		y = _y;
		z = _z;
		return 0;
	};
	inline VertexT IVertex()
	{
		return VertexT(x, y, z);
	}
private:
	float x, y, z;
};


class ReadSTLFile
{
public:
	ReadSTLFile();
	~ReadSTLFile();
	bool ReadFile(const char *cfilename);
	int NumTri();
	vector<Point3f>& PointList();
	vector<Point3f>& PointNormList();
private:
	vector<Point3f> pointList;
	vector<Point3f>pointNormList;
	unsigned int unTriangles;
	bool ReadASCII(const char *cfilename);
	bool ReadBinary(const char *cfilename);

	char* memwriter;
	int cpyint(const char*& p);
	float cpyfloat(const char*& p);
};


然后是STL类型数据的实现:ReadSTL.cpp

#include "ReadSTL.h"
#include <vector>
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>

ReadSTLFile::ReadSTLFile()
{
}


ReadSTLFile::~ReadSTLFile()
{
}

bool ReadSTLFile::ReadFile(const char *cfilename)
{
	FILE * pFile;
	long lSize;
	char* buffer;
	size_t result;

	/* 若要一个byte不漏地读入整个文件,只能采用二进制方式打开 */
	fopen_s(&pFile, cfilename, "rb");
	if (pFile == NULL)
	{
		fputs("File error", stderr);
		exit(1);
	}

	/* 获取文件大小 */
	fseek(pFile, 0, SEEK_END);
	lSize = ftell(pFile);
	rewind(pFile);

	/* 分配内存存储整个文件 */
	buffer = (char*)malloc(sizeof(char)*lSize);
	if (buffer == NULL)
	{
		fputs("Memory error", stderr);
		exit(2);
	}

	/* 将文件拷贝到buffer中 */
	result = fread(buffer, 1, lSize, pFile);
	if (result != lSize)
	{
		fputs("Reading error", stderr);
		exit(3);
	}

	/* 结束演示,关闭文件并释放内存 */
	fclose(pFile);

	ios::sync_with_stdio(false);
	if (buffer[79] != '\0')//判断格式
	{
		ReadASCII(buffer);
	}
	else
	{
		ReadBinary(buffer);
	}
	ios::sync_with_stdio(true);

	free(buffer);
	return true;
}

bool ReadSTLFile::ReadASCII(const char *buffer)
{
	unTriangles = 0;
	float x, y, z;
	int i;
	string name, useless;
	stringstream ss(buffer);
	ss >> name >> name >> name >> name;
	ss.get();
	do {
		ss >> useless;
		if (useless != "facet")
			break;
		ss >> useless >> x >> y >> z;
		pointNormList.push_back(Point3f(x, y, z));
		getline(ss, useless);
		getline(ss, useless);
		for (i = 0; i < 3; i++)
		{
			ss >> useless >> x >> y >> z;
			pointList.push_back(Point3f(x, y, z));
		}
		unTriangles++;
		getline(ss, useless);
		getline(ss, useless);
		getline(ss, useless);
	} while (1);
	return true;
}

bool ReadSTLFile::ReadBinary(const char *buffer)
{
	const char* p = buffer;
	char name[80];
	int i, j;
	memcpy(name, p, 80);
	p += 80;
	unTriangles = cpyint(p);
	for (i = 0; i < unTriangles; i++)
	{
//		p += 12;//跳过头部法向量
		pointNormList.push_back(Point3f(cpyfloat(p), cpyfloat(p), cpyfloat(p)));
		for (j = 0; j < 3; j++)//读取三顶点
			pointList.push_back(Point3f(cpyfloat(p), cpyfloat(p), cpyfloat(p)));
		p += 2;//跳过尾部标志
	}
	return true;
}

int ReadSTLFile::NumTri()
{
	return unTriangles;
}

vector<Point3f>& ReadSTLFile::PointList()
{
	return pointList;
}

vector<Point3f>& ReadSTLFile::PointNormList()
{
	return pointNormList;
}

int ReadSTLFile::cpyint(const char*& p)
{
	int cpy;
	memwriter = (char*)&cpy;
	memcpy(memwriter, p, 4);
	p += 4;
	return cpy;
}
float ReadSTLFile::cpyfloat(const char*& p)
{
	float cpy;
	memwriter = (char*)&cpy;
	memcpy(memwriter, p, 4);
	p += 4;
	return cpy;
}

最后是实现:

#include <iostream>
#include <fstream>
#include <vector>
#include <time.h>
#include <windows.h>
#include <stdio.h>
#include <vector>
#include<map>
#include <string>
#include"ReadSTL.h"

//自定义比较排序函数
class StlCmpVec
{
public:

	bool operator()(const LVertex3f& _v0, const LVertex3f& _v1) const
	{
		if (_v0.x == _v1.x)
		{
			if (_v0.y == _v1.y)
			{
				if (_v0.z == _v1.z)
				{
					return (_v0.z < _v1.z);
				}
				else
				{
					return (_v0.z < _v1.z);
				}
			}
			else return (_v0.y < _v1.y);
		}
		else return (_v0.x < _v1.x);
	}
};

void saveBinaryPLY(char* oupathply, float* pts, float* nor, const int face_num)
{
	//Create Vertex Index
	int verts_num = 0;
	std::vector<LVertex3f> outpts_(face_num * 3);
	std::vector<LVertexIndex> facetIndex(face_num);
	std::vector<LTexture2f> outtex_(face_num * 3);
	//StlCmpVec comp(0.000001f);
	std::map<LVertex3f, int, StlCmpVec> VertexIndexMap;
	std::map<LVertex3f, int, StlCmpVec> ::iterator  VertexIndexItr;
	LTriangle trianglevertex;
	LTexture triangletexture;
	LVertexIndex triangleVertexIndex;
	for (int i = 0; i < face_num; i++)
	{
		trianglevertex.vertex[0].x = pts[i * 9];
		trianglevertex.vertex[0].y = pts[i * 9 + 1];
		trianglevertex.vertex[0].z = pts[i * 9 + 2];
		trianglevertex.vertex[1].x = pts[i * 9 + 3];
		trianglevertex.vertex[1].y = pts[i * 9 + 4];
		trianglevertex.vertex[1].z = pts[i * 9 + 5];
		trianglevertex.vertex[2].x = pts[i * 9 + 6];
		trianglevertex.vertex[2].y = pts[i * 9 + 7];
		trianglevertex.vertex[2].z = pts[i * 9 + 8];
		for (int j = 0; j < 3; j++)
		{
			VertexIndexItr = VertexIndexMap.find(trianglevertex.vertex[j]);
			if (VertexIndexItr == VertexIndexMap.end())
			{
				outpts_[verts_num] = trianglevertex.vertex[j];//vertex
				outtex_[verts_num] = triangletexture.texture[j];
				triangleVertexIndex.index[j] = verts_num;
				VertexIndexMap[trianglevertex.vertex[j]] = verts_num;
				verts_num++;
			}
			else
				triangleVertexIndex.index[j] = VertexIndexItr->second;
		}
		facetIndex[i] = triangleVertexIndex;//index
	}
	outpts_.resize(verts_num);

	cout << "Ascii : verts_num = " << verts_num << endl;
	cout << "Ascii : face_num   = " << face_num << endl;
	cout << "Ascii : VertexIndexMap size " << VertexIndexMap.size() << endl;

	//write binary ply
	FILE* fpply;
	fopen_s(&fpply, oupathply, "wb");
	fprintf_s(fpply, "%s\n", "ply");
	fprintf_s(fpply, "%s %s\n", "format binary_little_endian ", "1.0");
	fprintf_s(fpply, "%s\n", "comment Export generated by Fussen");
	fprintf_s(fpply, "%s\n", "comment TextureFile texture.jpg");
	fprintf_s(fpply, "%s %d\n", "element vertex", verts_num);
	fprintf_s(fpply, "%s\n", "property float x");
	fprintf_s(fpply, "%s\n", "property float y");
	fprintf_s(fpply, "%s\n", "property float z");
	fprintf_s(fpply, "%s %d\n", "element face", face_num);
	fprintf_s(fpply, "%s\n", "property list uchar int vertex_indices");
	fprintf_s(fpply, "%s\n", "end_header");
	for (int i = 0; i < verts_num; ++i)
	{
		float temp[3];
		temp[0] = outpts_[i].x;
		temp[1] = outpts_[i].y;
		temp[2] = outpts_[i].z;
		fwrite((char const*)temp, sizeof(float), 3, fpply);
	}
	unsigned char value = (unsigned char)3;//ply format demand value must unsigned char and const char*
	for (int i = 0; i < face_num; ++i)
	{
		fwrite(reinterpret_cast<const char*> (&value), sizeof(unsigned char), 1, fpply);
		int temp[3];
		temp[0] = facetIndex[i].index[0];
		temp[1] = facetIndex[i].index[1];
		temp[2] = facetIndex[i].index[2];
		fwrite(reinterpret_cast<const char*> (temp), sizeof(int), 3, fpply);
	}
	fclose(fpply);
}

int main()
{
	cout << "Test Start..." << endl;
	//Test texture
	char *readpath = "../Test.stl";
	int vertpts_size = 0;
	float *vertpts = new float[MAXTRIANGLENUM * 9]();//vertpts_size 是顶点个数;vertpts_的排列方式是每个面片三个顶点的xyz坐标依次排列
	float *vertnor = new float[MAXTRIANGLENUM * 3]();
	unsigned char *vertcolor = new unsigned char[MAXTRIANGLENUM * 3]();
	ReadAsciiSTL(readpath, vertpts, vertnor, vertcolor, vertpts_size);
	cout << "Read Date end" << endl;
	char* oupathply = "Test.ply";
	int face_num = vertpts_size /  9;
	saveBinaryPLY(oupathply, vertpts, vertnor, face_num);
	delete[]vertpts;
	delete[]vertnor;
	delete[]vertcolor;
	cout << "Test Texture End." << endl;
	getchar();
	return 0;
}

 

在自定义比较函数StlCmpVec时,指定 StlCmpVec, Map建立时传入的是一个StlCmpVec类名,而不是一个函数或 functor 对象。这点要注意,然后里面的重载运算符(),只是用来做比较,返回值要符合Map的定义规则,不能直接返回true或者false。

另外借鉴了这篇文章:元素为自定义复合结构时 map,set 如何处理重复 key 及排序?

 

 

 

 

C++容器和泛型 一、顺序容器 1,标准库定义了3种类型的顺序容器:vector、list和deque。它们的差别主要在于访问元素的方式,以及添加或删除元素相关操作运算代价。标准库还提供了三种容器适配器:stack、queue和priority_queue。 2,将一个容器复制给另一个容器时,类型必须匹配,包括容器类型和元素类型。 vector ivec; vector dvec(ivec); //errorlis... 阅读详情

相关推荐

k8s RoCE 部署: k8s-rdma-shared-dev-plugin + macvlan cni

写给自己的入门篇 文章目录前言一、创建 k8s 集群二、启用 primary network三、启用 secondary networkk8s-rdma-shared-dev-pluginMultus CNISecondary CNIMulti-Network CRD四、启用 pod五、在 pod 中启动 RoCE 流量总结 前言 写给自己的扫盲篇。后续会在原理方面持续更新 一、创建 k8s 集群 k8s 集群的创建有多种方法,可以按照官方文档的说明来.

sunshuying1010的博客 9784

C++的map的自定义排序

map排序

sinat_31608641的博客 5378

手把手教你用Cursor + 自定义MCP工具链,让AI帮你搞定App逆向(附adb-mcp/frida-mcp源码)

本文详细介绍了如何利用Cursor IDE与自定义MCP工具链构建AI驱动的App逆向工作流,涵盖adb-mcp和frida-mcp的源码实现与实战应用。通过自然语言指令,AI可自动化完成设备控制、动态Hook和数据分析,显著提升移动安全测试效率。适合安全研究人员和开发者快速掌握智能逆向技术。

weixin_33737134的博客 1301

C++set容器-内置类型指定排序

set容器排序 利用访函数,可以改变排序规则 一、set存放内置数据类型 代码如下: #include <iostream> using namespace std; #include <set> class Mycompare { public: bool operator()(int v1, int v2) { return v1 > v2; } }; //set容器排序 void test01() { set<int >s1; s1.i

m0_51955470的博客 692

[特殊字符] 智能补货(AI-driven Replenishment)完整实施方案

补货量=预测需求+安全库存−当前库存补货量 = 预测需求 + 安全库存 - 当前库存补货量=预测需求+安全库存−当前库存。:基于 LSTM/Transformer 预测未来 30/60/90 天的销量。

nbsaas-boot基于Request-Response的企业级快速开发框架 967

时间序列分析:ARCH、GARCH、TGARCH与DCC-GARCH波动率模型(含代码)

关注TGARCH模型和DCC-GARCH的应用,通过在沪深300和中证500指数上应用TGARCH和DCC-GARCH模型,揭示这两个指数之间的条件异方差及动态相关性的变化规律。

celiaweiwei的博客 1万+

C++ STL中Map的按Key排序和按Value排序

转载自:IIcyZhao’s Road 原文地址:http://blog.youkuaiyun.com/iicy266/article/details/11906189map是用来存放< key, value >键值对的数据结构,可以很方便快速的根据key查到相应的value。 假如存储学生和其成绩(假定不存在重名,当然可以对重名加以区分),我们用map来进行存储就是个不错的选择。 我们这样定义:map

善战者无赫赫之功 的专栏 3663

TMC2209串口配置实战:从寄存器解析到电机精准控制

本文详细解析TMC2209步进电机驱动芯片的串口配置方法,从硬件连接到UART通信协议,再到核心寄存器功能详解。通过实战案例展示电流控制、微步细分和速度调节等关键配置,帮助工程师实现电机精准控制,并提供了常见故障诊断和优化技巧。

f6g7h8i9j的博客 1175

《C++笔记》 Part8 删除数组中重复元素的算法(map)

《C++笔记》 Part8 删除数组中重复元素的算法(map)原文链接: 1.去掉数组中重复出现元素的算法 2.使用C++的map容器遇到的bug(map/set iterator not dereferencable) 3.http://bbs.youkuaiyun.com/topics/250083746 <1、数组去重>一.问题描述在实际编程中我们经常遇到“去掉重复数字”等类似的问题,也就

梁的博客 5050

23种设计模式-代理(Proxy)设计模式

软考中级-软件设计师 代理设计模式

片言可决讼,半纸能缚龙 1784

电磁场仿真软件:Sonnet_(3).Sonnet用户界面与操作

Sonnet支持用户通过二次开发自定义工具,例如:编写Python脚本,实现自定义功能。将脚本保存为工具插件。在Sonnet中加载并使用自定义工具。# 示例:自定义工具 import sonnet def custom_tool(design , object , parameter , value) : """自定义工具示例,设置对象的指定参数。

kkchenjj的博客 1523

map的键使用自定义结构体

背景: map的定义 template, class A = allocator > 可见,map的定义是一个模板类,模板参数为Key,T,Pred,A,各参数的含义如下:Key---------键T------------值Pred--------map用于比较键的类,这个类必须重载()操作符,在其中定义比较操作..(虽然比较怪,不是重载大于小于号

学而不思则罔 2123

实战5:农作物产量预测 蔬菜产量预测 lstm 完整代码数据-可直接运行-计算机毕设

实战5:农作物产量预测 蔬菜产量预测 lstm 完整代码数据-可直接运行-计算机毕设

机器学习深度学习业余选手 1258

map和set【C++】

本文摘要: C++标准库中的关联式容器set和map基于红黑树实现,具有自动排序特性。set存储唯一元素,map存储键值对,两者插入、删除、查找时间复杂度均为O(logn)。set元素不可修改,map的value可改但key不可变。使用时需注意迭代器失效问题,删除元素后原迭代器会失效。map通过[]运算符可实现键值访问/插入,set/map支持自定义排序规则。底层实现上,set和map共用红黑树模板,通过仿函数区分键值比较逻辑。迭代器采用中序遍历方式实现,支持双向移动但不支持随机访问。

加油!!! 249

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

STL入门

标准C++语言中,STL被组织为下面的13个头文件:、、、、、、、、、、、及。与之前介绍的容器不同,在创建pair对象时,必须提供两个类型名,两个对应的类型名的类型不必相同。iterator 是STL中的迭代器,迭代器为一个变量,与指针类似,一般用来辅助对容器的操作,如遍历,查询等。

m0_72610323的博客 225

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

156 - Ananagrams

#include #include #include #include #include #include #include using namespace std; typedef long long ll; const int c = 'A' - 'a'; void deal(string &inp){     for(int i = 0;

u014136456的专栏 199

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

map集合遍历去重

我们可以使用 map 集合的特性来遍历去重。 例如,我们可以使用一个 map 集合来存储元素,并使用 map 集合的键来存储元素的值。每当我们遇到一个新元素时,我们就将其插入到 map 集合中。如果这个元素已经存在于 map 集合中,我们就不会再次插入它。 下面是一个例子: #include <iostream> #include <map> int main() { ...

weixin_35755434的博客 789

C++、map使用(数组中重复的数字)

C++的map是真难用 leetcode链接 题目: 找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 示例 1: 输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3 第一想法是循环,但是一直不会用C++的map 就试了下 class Solution { public: int findRepeatNumber(vec

qq_36255655的博客 1150

对c++中的map结构进行排序

map是key有序的结构,自动实现key的顺序和去重; 如果相对value排序,要自己定义pair结构。 #include <map> #include <string> #include <iostream> using namespace std; // 自己编写的Compare,实现按照字符串长度进行排序 struct CmpByKeyLength { bool operator()(const string& k1, const string&am

PETERPARKERRR的博客 964

C++中Map的常见用法

最近工作中,对map的使用比较频繁,所以对常见的map相关用法做一下整理 介绍 map是STL(中文标准模板库)的一个关联容器。 可以将任何基本类型映射到任何基本类型。 map提供一对一的数据处理,key-value键值对,其类型可以自己定义,第一个称为关键字,第二个为关键字的值 map内部是根据key自动排序的 声明 //第一个是键的类型(key),第二个是值的类型(value) map<type1name,type2name> maps; #include <map> usi

Here_JW的博客 1171

C++map与set

本章主要介绍了map和set的基础知识,并且列举了一些应用场景

kirotsupreme的博客 598

c++ map数据结构比较同义词去重,转换排除

方便大家.日后需要,可以找的到! #include "stdafx.h" #include #include #include #include using namespace std; string& replace_all_distinct(string& str,const string& old_value,const string& new_value)

a灬魂淡博客 1056

用map实现去重

map内部使用红黑树实现,因此可以实现极为快速的查找。可以利用map键值的惟一性将要去重的对象作为键值插入到map中,插入失败时即为重复值,不过对象要满足“严格弱排序”。一般要对象及对象中的属性对象的“ 返回值格式:         typedef std::map mapFilter;         mapFilter mapFilterInstance;

klarclm的专栏 1万+

【C++】map 的介绍

基础语法:定义 map 类型是有模板的,它可以接受三个参数第一个参数是键的数据类型第二个参数是值的数据类型第三个参数是排序规则,不写则默认按照键的升序排序举例:定义个 int string 按照键的降序排序的默认第三个参数是 less 如果想要降序排序就改为 greater。

2301_77562764的博客 359

突破编程_C++_STL教程( map 的实战应用)

C++ STL教程: map 的实战应用

突破编程 1262

set判重与map判重(C++)

set可以理解为一个不重复且有序的集合,实现原理红黑二叉树。一般我们对与set的使用集中在以下几种操作:1.声明或创建set集合 2.插入操作 3.删除操作 4。判断集合中是否有元素 5.返回集合中元素的个数 6.遍历整个集合。

weixin_63033110的博客 1094
上一篇: RGB 常用颜色对照表
下一篇: 【CUDA】grid、block、thread的关系及thread索引的计算
Walker Zhang
博客等级 码龄14年 144粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值