项目中碰到一个文件IO的问题,如下:
现有文本文件InputFile.txt,内容大致为:
//==================================================
//POV-Ray Geometry file
//==================================================
//This file has been created by PoseRay v3.13.21.571
//3D model to POV-Ray/Moray Converter.
//Author: FlyerX
//Email: flyerx_2000@yahoo.com
//Web: https://sites.google.com/site/poseray/
//==================================================
//Materials
#include "armadillo_POV_mat.inc"
//Geometry
#declare armadillo_unnamed_material_=mesh2{
vertex_vectors{
106289,
<2.312181,0.1683176,0.8163048>,
<2.311077,0.1786384,0.8170696>,
<2.308454,0.1687992,0.8194576>,
<2.283935,0.213596,0.821744>,
<2.27917,0.2198224,0.819728>,
<2.278454,0.2137072,0.8223384>,
<2.330702,0.2852856,0.7614728>,
<2.324658,0.2909416,0.7601728>,
<2.326384,0.2866528,0.7637816>,
<2.21661,0.3663198,0.6235906>,
<2.220123,0.3622722,0.6229381>,
<2.217081,0.3619105,0.6271858>,
<2.274469,0.1575984,0.6827952>,
<2.270182,0.1531384,0.6872344>,
<2.27057,0.1579048,0.6830072>,
<2.184279,0.3989262,0.6556019>,
<2.183541,0.3982444,0.657621>,
<2.18146,0.4030383,0.6557837>,
...
<-0.4670421,-0.5036201,-0.7268002>,
<0.6455061,0.7251832,-0.2396481>,
<-0.7230348,0.6187588,-0.3071779>,
<-0.7314488,0.5028389,-0.4605819>
}
face_indices{
212574,
<0,1,2>,
<3,4,5>,
<6,7,8>,
<9,10,11>,
<12,13,14>,
<15,16,17>,
<18,19,20>,
...
<103021,67168,97077>,
<106084,106276,105340>,
<106084,90380,106276>
}
normal_indices{
212574,
<0,1,2>,
<3,4,5>,
<6,7,8>,
...
}
inside_vector <0,0,1> }
//Model assembly from the meshes
#declare armadillo_=
object{armadillo_unnamed_material_ /*material{unnamed_material_}*/ hollow }
需求如下:
实现一个函数,读取该文本文件,从vertex_vectors中提取出其中的106289个顶点的坐标,比如从<2.312181,0.1683176,0.8163048>,中提取出Vector3f(2.312181,0.1683176,0.8163048); 所有的顶点会保存在vector<Vector3f> initial_vertices这样的一个vector中。程序每隔一段时间会计算出一个平移和旋转矩阵,然后根据initial_vertices计算出新值,保存在vector<Vector3f> updated_vertices中,之后新建一个新的文本文件OutputFile.txt,按照InputFile.txt格式输出,只是vertex_vectors中的所有顶点坐标用updated_vertices的值替换。
之前对文件操作和字符串处理不太熟悉,看了下C的IO操作,然后自己简单的实现了下,功能上基本可以满足自己的需求。以下为代码:
#include <vector>
#include <stdio.h>
#include <string.h>
using namespace std;
struct Vector3f
{
Vector3f(float x1, float x2, float x3)
:v1(x1), v2(x2), v3(x3)
{
}
float v1;
float v2;
float v3;
};
const size_t kMaxNum = 100; // 字符数组最大容量
char g_buffer[kMaxNum];
void ProcessVertices(const vector<Vector3f>& initial_vertices, vector<Vector3f>& updated_vertices)
{
const size_t num_vertices = initial_vertices.size();
for (int i = 0; i < num_vertices; ++i)
{
const Vector3f& tmp_vertex = initial_vertices[i];
Vector3f new_vertex(tmp_vertex.v1 + 10.0f, tmp_vertex.v2 + 10.0, tmp_vertex.v3 + 10.0);
updated_vertices.push_back(new_vertex);
}
}
void ExtractVertices( FILE* input_file, FILE* output_file, vector<Vector3f> &initial_vertices, vector<Vector3f>& updated_vertices )
{
// 逐行读取文件,直到读取的字符串含有vertex_vectors子串
do
{
char* result;
result = fgets(g_buffer, kMaxNum-1, input_file);
if (result)
{
fputs(g_buffer, output_file);
}
} while ((strstr(g_buffer, "vertex_vectors") == NULL));
// 从下一行读取num_vertices
Vector3f tmp_vertex(0.0f, 0.0f, 0.0f);
size_t total_num;
fgets(g_buffer, kMaxNum-1, input_file);
sscanf(g_buffer, "%d", &total_num);
// 接下来读取num_vertices行,并提取顶点坐标用于初始化initial_vertices
for (int i = 0; i < total_num; ++i)
{
fgets(g_buffer, kMaxNum-1, input_file);
sscanf(g_buffer, "<%f,%f,%f>,", &tmp_vertex.v1, &tmp_vertex.v2, &tmp_vertex.v3);
initial_vertices.push_back(tmp_vertex);
}
// 对顶点做变换
ProcessVertices(initial_vertices, updated_vertices);
// 输出更新后的顶点坐标
sprintf(g_buffer, "%d,\n", total_num);
fputs(g_buffer, output_file);
for (int i = 0; i < total_num; ++i)
{
const Vector3f& tmp_vertex = updated_vertices[i];
sprintf(g_buffer, "<%f,%f,%f>,\n", tmp_vertex.v1, tmp_vertex.v2, tmp_vertex.v3);
fputs(g_buffer, output_file);
}
}
void ExtractNormals( FILE* input_file, FILE* output_file, vector<Vector3f> &initial_normals, vector<Vector3f>& updated_normals )
{
// 然后再次逐行读取文件,直到读取的字符串含有normal_vectors子串
do
{
char* result;
result = fgets(g_buffer, kMaxNum-1, input_file);
if (result)
{
fputs(g_buffer, output_file);
}
} while ((strstr(g_buffer, "normal_vectors") == NULL));
// 从下一行读取num_normals
Vector3f tmp_vertex(0.0f, 0.0f, 0.0f);
size_t total_num;
fgets(g_buffer, kMaxNum-1, input_file);
sscanf(g_buffer, "%d", &total_num);
// 接下来读取num_normals行,并提取法线向量用于初始化initial_normals
for (int i = 0; i < total_num; ++i)
{
fgets(g_buffer, kMaxNum-1, input_file);
sscanf(g_buffer, "<%f,%f,%f>,", &tmp_vertex.v1, &tmp_vertex.v2, &tmp_vertex.v3);
initial_normals.push_back(tmp_vertex);
}
// 对法线做变换
ProcessVertices(initial_normals, updated_normals);
// 输出更新后的法线向量
sprintf(g_buffer, "%d,\n", total_num);
fputs(g_buffer, output_file);
for (int i = 0; i < total_num; ++i)
{
const Vector3f& tmp_vertex = updated_normals[i];
sprintf(g_buffer, "<%f,%f,%f>,\n", tmp_vertex.v1, tmp_vertex.v2, tmp_vertex.v3);
fputs(g_buffer, output_file);
}
}
void ReadRestContents( FILE* input_file, FILE* output_file )
{
// 读取文件剩余内容
while(fgets(g_buffer, kMaxNum-1, input_file))
{
fputs(g_buffer, output_file);
}
}
void PovrayGeomFileTransformation_new(const char* input_file_name, const char* output_file_name, vector<Vector3f>& initial_vertices, vector<Vector3f>& updated_vertices,
vector<Vector3f>& initial_normals, vector<Vector3f>& updated_normals)
{
FILE* input_file;
FILE* output_file;
input_file = fopen(input_file_name, "r");
if (input_file == NULL)
{
perror(input_file_name);
exit(EXIT_FAILURE);
}
output_file = fopen(output_file_name, "w+");
if (output_file == NULL)
{
fclose(input_file);
perror(output_file_name);
exit(EXIT_FAILURE);
}
ExtractVertices(input_file, output_file, initial_vertices, updated_vertices);
ExtractNormals(input_file, output_file, initial_normals, updated_normals);
ReadRestContents(input_file, output_file);
fclose(input_file);
fclose(output_file);
}
int main(int argc, char* argv[])
{
vector<Vector3f> initial_vertices;
vector<Vector3f> updated_vertices;
vector<Vector3f> initial_normals;
vector<Vector3f> updated_normals;
const char* input_file_name = "armadillo_POV_geom_backup.inc";
const char* output_file_name = "armadillo_POV_geom.inc";
PovrayGeomFileTransformation_new(input_file_name, output_file_name, initial_vertices, updated_vertices, initial_normals, updated_normals);
return 0;
}
PS:本来打算用C++的iostream, 看过 C++ 工程实践(7):iostream 的用途与局限 后决定还是用C的IO。
补充:
C语言对字符串数组的处理太麻烦,本文采用C++的string class来简化数据处理
#include <vector>
#include <stdio.h>
#include <string>
using namespace std;
struct Vector3f
{
Vector3f(float x1, float x2, float x3)
:v1(x1), v2(x2), v3(x3)
{
}
float v1;
float v2;
float v3;
};
typedef vector<Vector3f> RigidBodyGeom;
typedef vector<string> RigidBodyPovGeomContents;
const int kMaxNum = 100;
char g_buffer[kMaxNum];
void ReadContextPart( FILE* input_file, vector<string> &context_part, const char* vertices_type)
{
do
{
char* result;
result = fgets(g_buffer, kMaxNum, input_file);
if (result)
{
context_part.push_back(string(g_buffer));
}
} while ((strstr(g_buffer, vertices_type) == NULL));
}
void ExtractVerticesData( FILE* input_file, vector<Vector3f> &initial_vertices_data )
{
// First read num_vertices
int total_num;
if (fgets(g_buffer, kMaxNum, input_file))
{
if (1 != sscanf(g_buffer, "%d", &total_num)) // check if total num has been read correctly
{
printf("Error read num_vertices!");
exit(EXIT_FAILURE);
}
}
else
{
printf("Empty line for total_num!");
exit(EXIT_FAILURE);
}
Vector3f tmp_vertex(0.0f, 0.0f, 0.0f);
// Then read num_vertices rows and extract coordinates for initial_vertices_data initialization
for (int i = 0; i < total_num; ++i)
{
if (fgets(g_buffer, kMaxNum, input_file))
{
if (3 != sscanf(g_buffer, "<%f,%f,%f>,", &tmp_vertex.v1, &tmp_vertex.v2, &tmp_vertex.v3)) // check if all 3 coordinates have been read correctly
{
printf("Error read coordinates!");
exit(EXIT_FAILURE);
}
initial_vertices_data.push_back(tmp_vertex);
}
else
{
printf("Incorrect num_vertices rows!");
exit(EXIT_FAILURE);
}
}
}
void ReadRestContents( FILE* input_file, vector<string> &context_part)
{
while(fgets(g_buffer, kMaxNum, input_file))
{
context_part.push_back(string(g_buffer));
}
}
void ProcessVertices(const vector<Vector3f>& initial_vertices, vector<Vector3f>& updated_vertices)
{
const int num_vertices = initial_vertices.size();
for (int i = 0; i < num_vertices; ++i)
{
const Vector3f& tmp_vertex = initial_vertices[i];
Vector3f new_vertex(tmp_vertex.v1 + 10.0f, tmp_vertex.v2 + 10.0, tmp_vertex.v3 + 10.0);
updated_vertices.push_back(new_vertex);
}
}
void WriteContextPart(FILE* output_file, const vector<string> &context_part)
{
const int total_num = context_part.size();
for (int i = 0; i < total_num; ++i)
{
strcpy(g_buffer, context_part[i].c_str());
fputs(g_buffer, output_file);
}
}
void WriteContextVertices(FILE* output_file, const vector<Vector3f> &updated_vertices_data)
{
const int total_num = updated_vertices_data.size();
sprintf(g_buffer, "%d,\n", total_num);
fputs(g_buffer, output_file);
for (int i = 0; i < total_num; ++i)
{
const Vector3f& tmp_vertex = updated_vertices_data[i];
sprintf(g_buffer, "<%f,%f,%f>,\n", tmp_vertex.v1, tmp_vertex.v2, tmp_vertex.v3);
fputs(g_buffer, output_file);
}
}
void ReadInitialPovGeomFile(const char* input_file_name, vector<Vector3f>& initial_vertices, vector<Vector3f>& initial_normals,
vector<string>& context_part1, vector<string>& context_part2, vector<string>& context_part3)
{
FILE* input_file;
input_file = fopen(input_file_name, "r");
if (input_file == NULL)
{
perror(input_file_name);
exit(EXIT_FAILURE);
}
// read files line by line until encountering "vertex_vectors"
const char* kVertexVector = "vertex_vectors";
ReadContextPart(input_file, context_part1, kVertexVector);
ExtractVerticesData(input_file, initial_vertices);
// read files line by line until encountering "vertex_vectors"
const char* kNormalVector = "normal_vectors";
ReadContextPart(input_file, context_part2, kNormalVector);
ExtractVerticesData(input_file, initial_normals);
ReadRestContents(input_file, context_part3);
fclose(input_file);
}
void UpdateVerticesData(const vector<Vector3f>& initial_vertices, const vector<Vector3f>& initial_normals, vector<Vector3f> &updated_vertices, vector<Vector3f> &updated_normals)
{
ProcessVertices(initial_vertices, updated_vertices);
ProcessVertices(initial_normals, updated_normals);
}
void WriteUpdatedContents(const vector<string> &context_part1, const vector<string> &context_part2, const vector<string> &context_part3,
const vector<Vector3f>& updated_vertices, const vector<Vector3f>& updated_normals, const char* output_file_name)
{
FILE* output_file;
output_file = fopen(output_file_name, "w+");
if (output_file == NULL)
{
perror(output_file_name);
exit(EXIT_FAILURE);
}
WriteContextPart(output_file, context_part1);
// output updated vertices
WriteContextVertices(output_file, updated_vertices);
WriteContextPart(output_file, context_part2);
// output updated normals
WriteContextVertices(output_file, updated_normals);
WriteContextPart(output_file, context_part3);
fclose(output_file);
}
void ReadAllPovFiles(const vector<string>& input_file_names, vector<RigidBodyGeom>& rigid_body_initial_vertices, vector<RigidBodyGeom>& rigid_body_initial_normals,
vector<RigidBodyPovGeomContents>& rigid_body_context_part1, vector<RigidBodyPovGeomContents>& rigid_body_context_part2, vector<RigidBodyPovGeomContents>& rigid_body_context_part3)
{
const int num_names = input_file_names.size();
for (int i = 0; i < num_names; ++i)
{
vector<Vector3f> initial_vertices;
vector<Vector3f> initial_normals;
vector<string> context_part1;
vector<string> context_part2;
vector<string> context_part3;
ReadInitialPovGeomFile(input_file_names[i].c_str(), initial_vertices, initial_normals, context_part1, context_part2, context_part3);
rigid_body_initial_vertices.push_back(initial_vertices);
rigid_body_initial_normals.push_back(initial_normals);
rigid_body_context_part1.push_back(context_part1);
rigid_body_context_part2.push_back(context_part2);
rigid_body_context_part3.push_back(context_part3);
}
}
void UpdateAllPovVertices(const vector<RigidBodyGeom>& rigid_body_initial_vertices, const vector<RigidBodyGeom>& rigid_body_initial_normals,
vector<RigidBodyGeom>& rigid_body_updated_vertices, vector<RigidBodyGeom>& rigid_body_updated_normals)
{
const int num_rigid_bodies = rigid_body_initial_vertices.size();
for (int i = 0; i < num_rigid_bodies; ++i)
{
const vector<Vector3f>& initial_vertices = rigid_body_initial_vertices[i];
const vector<Vector3f>& initial_normals = rigid_body_initial_normals[i];
vector<Vector3f> updated_vertices;
vector<Vector3f> updated_normals;
UpdateVerticesData(initial_vertices, initial_normals, updated_vertices, updated_normals);
rigid_body_updated_vertices.push_back(updated_vertices);
rigid_body_updated_normals.push_back(updated_normals);
}
}
void WriteAllPovFiles(const vector<string>& output_file_names, vector<RigidBodyGeom>& rigid_body_updated_vertices, vector<RigidBodyGeom>& rigid_body_updated_normals,
const vector<RigidBodyPovGeomContents>& rigid_body_context_part1, const vector<RigidBodyPovGeomContents>& rigid_body_context_part2, const vector<RigidBodyPovGeomContents>& rigid_body_context_part3)
{
const int num_rigid_bodies = rigid_body_updated_vertices.size();
for (int i = 0; i < num_rigid_bodies; ++i)
{
const vector<Vector3f>& updated_vertices = rigid_body_updated_vertices[i];
const vector<Vector3f>& updated_normals = rigid_body_updated_normals[i];
WriteUpdatedContents(rigid_body_context_part1[i], rigid_body_context_part2[i], rigid_body_context_part3[i], updated_vertices, updated_normals, output_file_names[i].c_str());
}
}
int main(int argc, char* argv[])
{
vector<RigidBodyGeom> rb_initial_vertices;
vector<RigidBodyGeom> rb_initial_normals;
vector<RigidBodyGeom> rb_updated_vertices;
vector<RigidBodyGeom> rb_updated_normals;
vector<RigidBodyPovGeomContents> contexts_part1;
vector<RigidBodyPovGeomContents> contexts_part2;
vector<RigidBodyPovGeomContents> contexts_part3;
// mark POV files to be processed
vector<string> input_file_names;
input_file_names.push_back(string("Computer_POV_geom.inc"));
input_file_names.push_back(string("CSE_POV_geom.inc"));
input_file_names.push_back(string("Games_POV_geom.inc"));
input_file_names.push_back(string("International_POV_geom.inc"));
input_file_names.push_back(string("islandSCI1_POV_geom.inc"));
input_file_names.push_back(string("Journal_POV_geom.inc"));
input_file_names.push_back(string("R_POV_geom.inc"));
input_file_names.push_back(string("science_POV_geom.inc"));
input_file_names.push_back(string("Technology_POV_geom.inc"));
input_file_names.push_back(string("venus_POV_geom.inc"));
// determine output file names
string base_name, extension_name;
vector<string> output_file_names;
const int num_names = input_file_names.size();
for (int i = 0; i < num_names; ++i)
{
// search period in file name
string& file_name = input_file_names[i];
string::size_type idx = file_name.find('.');
if (idx == string::npos)
{
// file name does not contain any period
printf("File name error!");
exit(EXIT_FAILURE);
}
else
{
// split file name into base name and extension name
base_name = file_name.substr(0, idx);
extension_name = file_name.substr(idx+1);
output_file_names.push_back(base_name + string("0001") + "." + extension_name);
}
}
ReadAllPovFiles(input_file_names, rb_initial_vertices, rb_initial_normals, contexts_part1, contexts_part2, contexts_part3);
UpdateAllPovVertices(rb_initial_vertices, rb_initial_normals, rb_updated_vertices, rb_updated_normals);
WriteAllPovFiles(output_file_names, rb_updated_vertices, rb_updated_normals, contexts_part1, contexts_part2, contexts_part3);
return 0;
}