-2

I have array of struct in class,and I want save that in file. if I put the input ac.mem [i] .username except the username is stored in the file And if I put the input ac.mem [i] nothing will be saved. This is part of my code:

const int len=5;
class account {

public:

    struct members {

        string username;
        string password;
        int acsess;

    }mem[len];
};

class account  ac;
....
ac.mem[0] = { "admin","soran",5 };
    ac.mem[1] = { "hamid","hamid",4 };

    fstream acc1("account", ios::binary);
    for (int i = 0; i <= 1; i++) {

        acc1.write((char*)&ac.mem[i].username, sizeof(ac.mem[i].username));

    }
acc1.close();
....
ifstream acc2("account", ios::binary);
    for (int i = 0; i <= len; ++i) {
        acc1.read((char*)&ac.mem[i].username, sizeof(ac.mem[i].username));

        cout << i + 1 << "." << setw(10) << ac.mem[i].username << setw(20) << ac.mem[i].password << setw(20) << ac.mem[i].acsess << endl;

    }
    acc2.close();
Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • 1
    Side note: This is C++, you don't need to repeat `class` when defining objects, just use the typename on its own. And: About [`using namespace std`](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)... – Aconcagua May 03 '22 at 16:36
  • 3
    `acc1.write((char*)&ac.mem[i].username, sizeof(ac.mem[i].username));` If it contains a non POD type like `std::string` this wont work directly. One clue is sizeof() is a compile time constant meaning the structure is the same size if you have an empty string or one with a GB of data. – drescherjm May 03 '22 at 16:39
  • 1
    And @drescherjm basically stated why none of this code will work. It looks like you were reading `C`-language related material, and trying to apply it to C++. You should forget about anything you wrote here, and concentrate on writing the *data* that the struct represents to the file, not the `struct` itself. – PaulMcKenzie May 03 '22 at 16:46
  • Writing to and reading from a file is part of *serialization*. Search the internet for "C++ serialization" for explanations and libraries. – Thomas Matthews May 03 '22 at 16:51

2 Answers2

0

std::string objects are pretty complex types – they internally maintain pointers to memory. When you just write the internal representation to a file (casting address of to char*) all you write out are these pointers plus possibly some additional management data.

The actual string contents, though, are stored at the locations these pointers point to. When reading back you cannot ever assume to find the same data at the address you've just restored from file (unless the original string object written to still exists – but then playing around with the internals will, if two different std::string objects involved, with 100% probability lead to undefined behaviour due to double deletion, if not reading and writing them from/to memory that way already is).

What you actually want to print to file are the contents of the string – which you get by either std::string::c_str or alternatively std::string::data. You might additionally want to include the terminating null character (hopefully there are no internal ones within the string...) to be able to read back multiple strings, stopping reading each one at exactly the null terminator, then writing to file might look like:

std::string s;   // assign some content...
std::ofstream f; // open some path
if(f)            // stream opened successfully?
{
    f.write(s.c_str(), s.length() + 1);
}

Note that std::string::length returns the length without the terminating null character, so if you want/need to include it, you need to add one to as done above.

Alternatively you can write out the string's length first and then skip writing the null character – with the advantage that on reading back you already know in advance how many characters to read and thus to pre-allocate within your objects (std::string::reserve). For compatibilty reasons over different compilers and especially machines make sure to write out fixed-size data types from <cstdint> header, e.g.:

uint32_t length = s.length();
f.write(reinterpret_cast<char const*>(&length), sizeof(length));
f.write(s.c_str(), s.length());

This approach covers internally existing null characters as well (though if such data exists, std::vector<unsigned char> or preferably std::vector<uint8_t> might be better alternative, std::string is intended for texts).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • Another method is to write the length of the string first, then block write the string contents. This is nice because upon reading, you read the length so you know how much space to allocate before reading and you can block read into the allocated space. – Thomas Matthews May 03 '22 at 16:53
0

If you want to use C language, you could refer to the following code.

#include <stdio.h>
#include <stdlib.h>
#pragma warning(disable : 4996)
typedef struct {

    char* name;
    int phone;
}address;
int main(void)
{
    int i;
    address a[3];
    for (i = 0; i < 3; i++)
    {
        a[i].name = "jojo";
        a[i].phone = "123456";
    }
        
        FILE* fp;
        fp = fopen("list.txt", "ab"); 
    
        for (i = 0; i < 3; i++)
        {
            printf(" % s, % d",a[i].name,a[i].phone);
            fwrite(&a[i], sizeof(address), 1, fp); 
        }        
        fclose(fp);
    return 0;
}
Yujian Yao - MSFT
  • 945
  • 1
  • 3
  • 9