БлогNot. Файл с массивом из объектов класса, чтение и запись

Файл с массивом из объектов класса, чтение и запись

Грубо учебное, просто, чтобы не выкидывать.

Задача состоит в том, чтобы сохранить в файл и прочитать из файла массив, состоящий из объектов класса 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 [2332 просмотра]


теги: c++ программирование учебное

К этой статье пока нет комментариев, Ваш будет первым