Файл с массивом из объектов класса, чтение и запись
Грубо учебное, просто, чтобы не выкидывать.
Задача состоит в том, чтобы сохранить в файл и прочитать из файла массив, состоящий из объектов класса Test
.
У класса при этом есть свойства разных типов данных, например, числовое и строковое.
Коды проверены в консолях QT5 и Visual Studio 2015.
Реализация 1, с массивом char
и текстовым файлом FILE *
.
Буфер под строку-свойство s
для простоты имеет фиксированный размер SIZE
, а при чтении и записи строковую величину мы обрабатываем посимвольно. Код примитивен и довольно неустойчив к ошибкам. Свойства класса здесь тоже публичны, как в структуре.
Читается и пишется текстовый файл, в одной строке располагается запись о свойствах одного объекта класса.
#define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #define SIZE 30 class Test { public: int d; char s[SIZE]; friend int save(char *fname, Test *data, int n) { FILE *f = fopen(fname, "wt"); if (!f) return -1; for (int i = 0; i<n; i++) { fprintf(f, "%d ", data[i].d); for (int j = 0; j<strlen(data[i].s); j++) fputc(data[i].s[j], f); fprintf(f, "\n"); } fclose(f); return 0; } friend int load(char *fname, Test *data, int n) { FILE *f = fopen(fname, "rt"); if (!f) return -1; int i = 0; while (1) { fscanf(f, "%d ", &data[i].d); for (int j = 0; j<SIZE - 1; j++) { int c = fgetc(f); if (c == '\n') { data[i].s[j] = '\0'; break; } else data[i].s[j] = c; } data[i].s[SIZE - 1] = '\0'; fscanf(f, "\n"); if (feof(f) || i == n - 1) break; i++; } fclose(f); return 0; } }; int main() { Test obj[2]; obj[0].d = 1; strcpy(obj[0].s, "string 1"); obj[1].d = 2; strcpy(obj[1].s, "string 2"); save("data.txt", &obj[0], 2); Test obj2[2]; load("data.txt", &obj2[0], 2); for (int i = 0; i<2; i++) { printf("\n%d ", obj2[i].d); puts(obj2[i].s); } getchar(); return 0; }
Реализация 2, со строкой string
и текстовым потоком fstream
.
Основная сложность при таком подходе связана с тем, что в одной строке текстового файла могут быть разнотипные данные - в нашем случае, это число и строка, которая тоже может содержать разделители.
Если мы читаем данные через istream::getline, это придётся делать в буфер типа char *
, а
если применить std::getline, то в объект string
прочитается вся строка файла сразу и её потом всё равно придётся разбивать на части.
#include <iostream> #include <fstream> #include <string> #define SIZE 128 using namespace std; class Test { int d; string s; public: void putD(int d) { this->d = d; } void putS(char *s) { this->s = s; } int getD() { return d; } const char *getS() { return s.c_str(); } friend int save(char *fname, Test *data, int n) { ofstream f; f.open(fname); if (!f) return -1; for (int i = 0; i<n; i++) { f << data[i].d << " " << data[i].s; if (i<n - 1) f << endl; } f.close(); return 0; } friend int load(char *fname, Test *data, int n) { ifstream f; f.open(fname); if (!f) return -1; int i = 0; char buf[SIZE]; while (1) { f >> data[i].d; f.getline(buf, sizeof(buf)); data[i].s = string(buf); if (f.eof() || i == n - 1) break; i++; //или std::getline, но тогда полученный string нужно "разбирать" } f.close(); return 0; } }; int main() { Test obj[2]; obj[0].putD(1); obj[0].putS("string 1"); obj[1].putD(2); obj[1].putS("string 2"); save("data.txt", &obj[0], 2); Test obj2[2]; load("data.txt", &obj2[0], 2); for (int i = 0; i<2; i++) { printf("\n%d ", obj2[i].getD()); puts(obj2[i].getS()); } cin.get(); return 0; }
Реализация 3, со строкой string
и бинарным потоком fstream
, пожалуй, это предпочтительный вариант.
Заодно код показывает, как писать в бинарный поточный файл простую величину (int
) и составную (string
) и как потом читать их.
#include <iostream> #include <fstream> #include <string> using namespace std; class Test { int d; string s; public: void putD(int d) { this->d = d; } void putS(char *s) { this->s = s; } int getD() { return d; } string getS() { return s; } friend int save(char *fname, Test *data, int n) { ofstream f; f.open(fname, ios::binary); if (!f) return -1; int d; string s; for (int i = 0; i<n; i++) { d = data[i].getD(); s = data[i].getS(); f.write(reinterpret_cast<const char *>(&d), sizeof(d)); //пишем скалярную величину int len = s.size(); f.write(reinterpret_cast<const char *>(&len), sizeof(len)); f.write(s.c_str(), s.size()); //пишем string в бинарный файл } f.close(); return 0; } friend int load(char *fname, Test *data, int n) { ifstream f; f.open(fname, ios::binary); if (!f) return -1; int i = 0, len; while (!f.eof() && i<n) { f.read(reinterpret_cast<char *>(&data[i].d), sizeof(data[i].d)); //читаем скалярную величину из бинарного файла f.read(reinterpret_cast<char *>(&len), sizeof(len)); data[i].s.resize(len); f.read(&data[i].s[0], len); //читаем string из бинарного файла i++; } f.close(); return 0; } }; int main() { Test obj[2]; obj[0].putD(1); obj[0].putS("String number 1"); obj[1].putD(2); obj[1].putS("String twooo"); save("data.dat", &obj[0], 2); Test obj2[2]; load("data.dat", &obj2[0], 2); for (int i = 0; i<2; i++) { cout << endl << obj2[i].getD() << " " << obj2[i].getS(); } cin.get(); return 0; }
06.03.2018, 17:00 [2478 просмотров]