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 :
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;
}