Чтение пользовательского текстового файла ресурсов построчно - PullRequest
0 голосов
/ 25 января 2019

Мне нужно прочитать содержимое текстового файла и сохранить значения в переменных. Рассмотрим этот простой файл:

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);

и данные сохраняются в двух векторах, каждый из которых имеет три значения.

Теперь я хотел бы сделать то же самое, но с файлом, который включен в качестве пользовательского ресурса. Во-первых, я следую этому примеру , чтобы загрузить данные в буфер. Проблема в том, что я не знаю, что делать с этим буфером, чтобы получить данные и сохранить их аналогичным образом. Я пытался использовать функцию 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 с? Или есть более простая альтернатива, которую я должен рассмотреть?

1 Ответ

0 голосов
/ 25 января 2019

При использовании sscanf_s() вы передаете ему один и тот же указатель buffer каждый раз, поэтому он продолжает повторное чтение с одного и того же значения 2 снова и снова.

Вам необходимо продвинуть указательпосле каждого чтения.Возвращаемое значение sscanf_f() - это число полей для чтения , но вместо этого вам понадобится количество символов , которое вы можете получить с помощью спецификатора формата %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 ++ вместо ввода-вывода в стиле C.Например, вы можете присвоить данные 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.

Или, если вы хотите избежать затрат на выделение копии std::stringbuffer данных, вы можете написать собственный std::basic_streambuf класс (или найти стороннюю реализацию), который может читать из вашего 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 ++, вы можете значительно упростить задачу, используя контейнеры и алгоритмы из стандартной библиотеки 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
...