逐行读取用户定义的文本资源文件

Reading a user-defined text resource file line by line

我需要读取文本文件的内容并将值存储到变量中。考虑这个简单的文件:

2
-0.5    -0.5     0.0
 0.5    -0.5     0.0

当指定它的文件名时(因此不使用资源),我像这样进行 fscanf_s:

FILE *pFile;
fopen_s(&pFile, filename.c_str(), "r");

fscanf_s(pFile, "%d", &nPoints);
points = new Vec3[nPoints];

for (int n = 0; n < nPoints; ++n)
    fscanf_s(pFile, "%lf %lf %lf", &points[n][0], &points[n][1], &points[n][2]);

fclose(pFile);

并将数据保存到两个向量中,每个向量具有三个值。

现在,我想做同样的事情,但要使用作为用户定义资源包含的文件。首先,我按照 this example 将数据加载到缓冲区中。问题是我不知道如何处理这个缓冲区来检索数据并以类似的方式保存它。我试过使用 sscanf_s 函数:

sscanf_s(buffer, "%d", &nPoints);
points = new Vec3[nPoints];

for (int n = 0; n < nPoints; ++n) {
    sscanf_s(buffer, "%lf %lf %lf", &points[n][0], &points[n][1], &points[n][2]);
}

但它似乎并没有像我预期的那样工作。点数已正确读入 nPoints 变量,但两个向量最终的值为 2、-0.5、-0.5。

如何将缓冲区中的值保存到我的 Vec3 中?或者有没有我应该考虑的更简单的替代方案?

当使用 sscanf_s() 时,您每次都将相同的 buffer 指针传递给它,因此它会一遍又一遍地从相同的 2 值重新读取。

每次读取后需要将指针前移。 sscanf_f() 的 return 值是读取的 字段数 ,但您需要 消耗的字符数 ,您可以使用 %n 格式说明符获得,例如:

char *ptr = buffer;
int consumed;

sscanf_s(ptr, "%d%n", &nPoints, &consumed);
ptr += consumed;

points = new Vec3[nPoints];

for (int n = 0; n < nPoints; ++n) {
    sscanf_s(ptr, "%lf %lf %lf%n", &points[n][0], &points[n][1], &points[n][2], &consumed);
    ptr += consumed;
}

更好的选择是使用 C++ 风格 I/O 而不是 C 风格 I/O。例如,您可以将 buffer 数据分配给 std::istringstream,然后从 1 读取数据,例如:

#include <sstream>

std::istringstream iss(buffer);

iss >> nPoints;
points = new Vec3[nPoints];

for (int n = 0; n < nPoints; ++n) {
    iss >> points[n][0] >> points[n][1] >> points[n][2];
}

1:要从文件中读取,只需将 std::istringstream 替换为 std::ifstream

或者,如果您想避免分配 buffer 数据的 std::string 副本的开销,您可以 write a custom std::basic_streambuf class (或找到第 3 方实现)可以读取来自您的buffer(或者更好,直接来自原始资源),例如:

#include <iostream>

SomeCharArrayStreamBuf buf(buffer, size);
std::istream is(&buf);

is >> nPoints;
points = new Vec3[nPoints];

for (int n = 0; n < nPoints; ++n) {
    is >> points[n][0] >> points[n][1] >> points[n][2];
}

一旦您切换到使用 C++ I/O 流,您可以通过使用 C++ 标准库中的容器和算法进一步大大简化事情,例如:

#include <vector>

std::vector<Vec3> points; // <-- instead of 'Vec3 *pointers;'

#include <iostream>
#include <algorithm>
#include <iterator>

std::istream& operator>>(std::istream &in, Vec3 &v)
{
    in >> v[0] >> v[1] >> v[2];
    return in;
}

strm >> nPoints; // <-- where strm is your chosen istream class object
points.reserve(nPoints); // <-- instead of 'new Vec3[nPoints];'

std::copy_n(std::istream_iterator(strm), nPoints, std::back_inserter(points)); // <-- instead of a manual reading loop