Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Problem with an array of structs where the member is string, in binary file c++

I did a project that doesn’t work, so I tried another one to search for what the problem was.
I came to realize two things, one is that reading one string is no problem. But When I write 3 strings from a struct, then problems begin. I would like to understand why strings are so problematic. I guess it has something to do with the c way in c++ but idk. Or I’m just (probably) missing something. Also, the program in fact read all the input correctly but after that crash.

 #include <iostream>
using namespace std;

struct Alumno{

   string nombre;
};

int main()
{
  Alumno a[3];
  FILE* f = fopen("prueba", "wb");
  if(!f)
  {
      cout<<"Error: No se pudo abrir"<<endl;
      return 1;
  }
    for(int i = 0 ; i < 3 ; i ++)
    {
      cout<<"Name: ", cin>>a[i].nombre;//Input: Facundo Juan Lucas
     fwrite(&a[i].nombre, sizeof(a), 1, f);
    }
  fclose(f);
  cout<<endl<<endl;
  f = fopen("prueba", "rb");
  if(!f)
  {
      cout<<"Error: No se pudo abrir"<<endl;
      return 1;
  }
  int i = 0;
  while(fread(&a, sizeof(Alumno), 1, f))
    cout<<a[i].nombre<<endl, i++;//Expected output: Facundo Juan Lucas
  fclose(f);

    return 0;
}

>Solution :

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

The kind of I/O you are trying to do only works with simple POD types, but std::string is not a simple POD type. It contains a pointer to its character data which may be stored elsewhere in memory. So, you can’t read/write a string object as-is. You would just be reading/writing the object’s internal pointer, not the character data it is pointing at. You need to serialize its character data instead, eg:

#include <iostream>
#include <string>
#include <cstdio>
using namespace std;

struct Alumno{
    string nombre;
};

bool writeSizeT(FILE *f, size_t value) {
    return (fwrite(&value, sizeof(value), 1, f) == 1);
}

bool readSizeT(FILE *f, size_t& value) {
    return (fread(&value, sizeof(value), 1, f) == 1);
}

bool writeString(FILE *f, const string &value) {
    size_t size = value.size();
    if (!writeSizeT(f, size)) return false;
    return (fwrite(value.c_str(), size, 1, f) == 1);
}

bool readString(FILE *f, string &value) {
    size_t size;
    if (!readSizeT(f, size)) return false;
    value.resize(size);
    if (size == 0) return true;
    return (fread(value.data()/* or: &value[0] */, size, 1, f) == 1);
}

int main()
{
    Alumno a[3];

    FILE* f = fopen("prueba", "wb");
    if (!f)
    {
        cout << "Error: No se pudo abrir" << endl;
        return 1;
    }

    writeSizeT(3);
    for(int i = 0; i < 3; ++i)
    {
        cout << "Name: ";
        cin >> a[i].nombre;
        writeString(f, a[i].nombre);
    }

    fclose(f);
    cout << endl << endl;

    f = fopen("prueba", "rb");
    if (!f)
    {
        cout << "Error: No se pudo abrir" << endl;
        return 1;
    }

    if (readSizeT(f, count)) {
        if (count > 3) count = 3;
        size_t i = 0;
        while ((i < count) && readString(f, a[i].nombre)) {
            cout << a[i].nombre << endl;
            ++i;
        }
    }

    fclose(f);

    return 0;
}

That being said, you really should consider using C++-style file I/O instead of C-style file I/O, eg:

#include <iostream>
#include <string>
using namespace std;

struct Alumno{
    string nombre;
};

ostream& operator<<(ostream &os, const Alumno &a) {
    size_t size = a.nombre.size();
    os.write(reinterpret_cast<char*>(&size), sizeof(size));
    os.write(a.nombre.c_str(), size);
    return os;
}

istream& operator>>(istream &is, Alumno &a) {
    size_t size;
    if (is.read(reinterpret_cast<char*>(&size), sizeof(size))) {
        a.nombre.resize(size);
        if (size > 0)
            is.read(a.nombre.data()/* or: &a.nombre[0] */, size);
    }
    return is;
}

int main()
{
    Alumno a[3];

    ofstream f_out("prueba", ios::binary);
    if (!f_out.is_open())
    {
        cout << "Error: No se pudo abrir" << endl;
        return 1;
    }

    size_t count = 3;
    f_out.write(reinterpret_cast<char*>(&count), sizeof(count));
    for(int i = 0; i < 3; ++i)
    {
        cout << "Name: ";
        cin >> a[i].nombre;
        f_out << a[i];
    }

    f_out.close();
    cout << endl << endl;

    ifstream f_in("prueba", ios::binary);
    if (!f_in.is_open())
    {
        cout << "Error: No se pudo abrir" << endl;
        return 1;
    }

    if (f_in.read(reinterpret_cast<char*>(&count), sizeof(count))) {
        if (count > 3) count = 3;
        size_t i = 0;
        while ((i < count) && (f_in >> a[i])) {
            cout << a[i].nombre << endl;
            ++i;
        }
    }

    f_in.close();

    return 0;
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading