❥关于C++之stringstream典型用法

typedef basic_stringstream<char> stringstream;

类描述:

流类对字符串进行操作
此类的对象使用包含字符序列的字符串缓冲区。可以使用成员str 作为string对象直接访问此字符序列。
可以使用输入和输出流上允许的任何操作来插入和/或从流中提取字符。

一个典型使用示例:

下面图片展示的是一个3D模型的OBJ格式的文件内容:
蓝色v 开头的是三角形顶点坐标(x, y, z);
红色vt 开头的是所有顶点的纹理坐标(纹理坐标列表比顶点列表长的原因是一些顶点参与多个三角形,并且在这些情况下可能使用不同的纹理坐标);
绿色vn 开头的是顶点法向量(该列表通常也比顶点列表长,尽管在该示例中不是这样,同样是因为一些顶点参与多个三角形,并且在那些情况下可能使用不同的法向量);
紫色f 开头的是面。

[面]格式表示的含义:

格式:f 顶点1/纹理1/法向量1  顶点2/纹理2/法向量2  顶点3/纹理3/法向量3
其中:顶点1(纹理1,法向量1) — 顶点2(纹理2,法向量2) — 顶点3(纹理3,法向量3) 构成一个三角形。

现在编写程序,解析出OBJ文件中所有的顶点、顶点的纹理坐标、顶点的法向量,分类存储,以便可以用OpenGL程序绘制这个3D模型。

// parser.h
#ifndef PARSER_H_
#define PARSER_H_
#include <vector>
class ModelParser {
private:
    std::vector<float> vVals;// 所有单个的[顶点坐标]值
    std::vector<float> vtVals;// 所有单个的[纹理坐标]值
    std::vector<float> vnVals;// 所有单个的[法向量]值
    std::vector<float> triangleVerts;// #三角形#数据
    std::vector<float> textureCoords;// #纹理坐标#数据
    std::vector<float> normals;// #法向量#数据
public:
    void parseOBJ(const char* filePath);
    int getNumVertices();
	std::vector<float> getVertices();
	std::vector<float> getTextureCoordinates();
	std::vector<float> getNormals();
}
#endif

#include <fstream>
#include <stringstream>
#include <string>// std::string, std::stoi
#include "parser.h"
using namespace std;
void ModelParser::parseOBJ(const char* filePath) {
	float x, y, z;
	string content;
	ifstream fileStream(filePath, ios::in);
	string line = "";
	while (!fileStream.eof()) {
		getline(fileStream, line);
		if (line.compare(0, 2, "v ") == 0) {
			stringstream ss(line.erase(0, 1));
			ss >> x; ss >> y; ss >> z;
			vVals.push_back(x);
			vVals.push_back(y);
			vVals.push_back(z);
		}
		if (line.compare(0, 2, "vt") == 0) {
			stringstream ss(line.erase(0, 2));
			ss >> x; ss >> y;
			vtVals.push_back(x);
			vtVals.push_back(y);
		}
		if (line.compare(0, 2, "vn") == 0) {
			stringstream ss(line.erase(0, 2));
			ss >> x; ss >> y; ss >> z;
			vnVals.push_back(x);
			vnVals.push_back(y);
			vnVals.push_back(z);
		}
		if (line.compare(0, 2, "f ") == 0) {
			string oneData, v, vt, vn;
			stringstream ss(line.erase(0, 2));
			for (int i = 0; i < 3; i++) {
				getline(ss, oneData, ' ');
				stringstream oneDataS(oneData);
				getline(oneDataS, v, '/');
				getline(oneDataS, vt, '/');
				getline(oneDataS, vn, '/');

				int vertIndex = (stoi(v) - 1) * 3;
				int textureIndex = (stoi(vt) - 1) * 2;
				int normalIndex = (stoi(vn) - 1) * 3;

				triangleVerts.push_back(vVals[vertIndex]);
				triangleVerts.push_back(vVals[vertIndex + 1]);
				triangleVerts.push_back(vVals[vertIndex + 2]);

				textureCoords.push_back(vtVals[textureIndex]);
				textureCoords.push_back(vtVals[textureIndex + 1]);

				normals.push_back(vnVals[normalIndex]);
				normals.push_back(vnVals[normalIndex + 1]);
				normals.push_back(vnVals[normalIndex + 2]);
			}
		}
	}
}

int ModelParser::getNumVertices() { return (triangleVerts.size() / 3); }
vector<float> ModelParser::getVertices() { return triangleVerts; }
vector<float> ModelParser::getTextureCoordinates() { return textureCoords; }
vector<float> ModelParser::getNormals() { return normals; }

本文不涉及OpenGL,下面用纯C++,给OpenGL编程做好数据准备:

// encapsulation.h
#include <vector>
#include <glm\glm.hpp>// glm::vec2、glm::vec3
using namespace std;
class EncapModel {
private:
	int numVertices;
	vector<glm::vec3> vertices;
	vector<glm::vec2> texCoords;
	vector<glm::vec3> normalVecs;
public:
	ImportedModel() {}
	ImportedModel(const char *filePath);
	int getNumVertices();
	vector<glm::vec3> getVertices();
	vector<glm::vec2> getTextureCoords();
	vector<glm::vec3> getNormals();
};
#include <glm\glm.hpp>
#include "imported.h"
using namespace std;
EncapModel::EncapModel(const char *filePath) {
	ModelParser modelParser = ModelParser();
	modelParser.parseOBJ(filePath);

	numVertices = modelParser.getNumVertices();
	std::vector<float> verts = modelParser.getVertices();
	std::vector<float> tcs = modelParser.getTextureCoordinates();
	std::vector<float> normals = modelParser.getNormals();

	for (int i = 0; i < numVertices; i++) {
		vertices.push_back(glm::vec3(verts[i*3], verts[i*3+1], verts[i*3+2]));
		texCoords.push_back(glm::vec2(tcs[i*2], tcs[i*2+1]));
		normalVecs.push_back(glm::vec3(normals[i*3], normals[i*3+1], normals[i*3+2]));
	}
}

int EncapModel::getNumVertices() { return numVertices; }
vector<glm::vec3> EncapModel::getVertices() { return vertices; }
vector<glm::vec2> EncapModel::getTextureCoords() { return texCoords; }
vector<glm::vec3> EncapModel::getNormals() { return normalVecs; }

至此,封装完成,可以直接获所有顶点的三维(vec3)坐标数据、所有顶点的二维(vec2)纹理坐标数据、所有顶点的三维(vec3)法向量数据。第个顶点vec3(x,y,z)都对应有一个纹理坐标vec(s,t)和一个法向量vec3(nx,ny,nz)。解析、封装完毕!


示例中一些成员函数分析:

#include <string>// getline、stoi
✎[v 1.000000 -1.000000 -1.000000]
line.compare(0, 2, "v ")
line.erase(0, 1)
✎[f 2/1/1 3/2/1 4/3/1]
getline(ss, oneData, ' ');
stringstream oneDataS(oneData);
getline(oneDataS, v, '/');
getline(oneDataS, vt, '/');
getline(oneDataS, vn, '/');
int vertIndex = (stoi(v) - 1) * 3;
——————————————————————————————
函数原型:int compare(size_t pos, size_t len, const string& str) const;
函数原型:string& erase(size_t pos = 0, size_t len = npos);
函数原型:istream& getline(istream&& is, string& str, char delim);
函数原型:int stoi(const string& str, size_t* idx = 0, int base = 10);// stoi:StringToInteger

##############################
#include <stringstream>
stringstream ss(line.erase(0, 1));
ss >> x; ss >> y; ss >> z;
——————————————————————————————
构造函数原型:
explicit stringstream(const string& str,
                       ios_base::openmode which = ios_base::in | ios_base::out);
std::istream::operator>>
运算符>>是从istream类继承而来,并未重载之,故和istream中的>>具有完全相同的功能和特性。
比如,和cin>>一样,默认使用空白(空格、制表符和换行符)来确定字符串的结束位置,并清除遇到的空白缓冲。

stringstream类还有一个公有成员str,用来get/set内容。

string str() const;
void str(const string& s);
————————————————————————————
第一种形式(1)返回一个字符串对象,其中包含流当前内容的副本。
第二种形式(2)将s设置为流的内容,丢弃之前的任何内容。
对象保留其打开模式:如果这包括ios_base::ate,则写入位置将移动到新序列的末尾。

在内部,该函数调用其内部字符串缓冲区对象的str成员。
// stringstream::str
#include <string>
#include <iostream>
#include <sstream>// std::stringstream, std::stringbuf
int main () {
  std::stringstream ss;
  ss.str("Example string");
  std::string s = ss.str();
  std::cout << s << '\n';// 打印:Example string
  return 0;
}

 读入stringstream:(用"<<"而非">>")

std::ifstream vShaderFile;
vShaderFile.open(vertexPath);// open files
std::stringstream vShaderStream;
// read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
const char* vertexPath = vShaderStream.str();
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzyjr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值