debug read/write string to binary file

2019-02-17 23:56发布

问题:

I am trying to write to a binary file , here is my snippet of code

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

struct user
{
    string ID;
    string password;    
};

int main()
{
    fstream afile;
    afile.open("user.dat",ios::out|ios::binary);

    user person;

    person.ID ="001";

    person.password ="abc";

    afile.write (reinterpret_cast <const char *>(&person), sizeof (person));

    person.ID ="002";

    person.password ="def";

    afile.write (reinterpret_cast <const char *>(&person), sizeof (person));

    afile.close();

    afile.open("user.dat",ios::in|ios::binary);


    while (afile.read (reinterpret_cast <char *>(&person), sizeof (person)))
    {
        cout<<person.ID
            <<" "
            <<person.password
            <<endl;

    }

}

I am expecting my console output to be

001 abc
002 def

Instead i am getting

002 def 
002 def

Can someone explain to me?

回答1:

std::string is a class and the object of it doesn't store the content of the string directly.

It's implementation defined by for your case, for simplicity, you can understand it this way:

std::string has a member that stores the pointer(say ptr) to the actual data.

and

   std::string s = "001";

would not point ptr to the address string of "001"; it would allocate memory and copy the string into that memory. Then when you do

    s = "002";

it doesn't need to reallocate memory to store "002"; it just copy "002" to the memory that stores "001" previously.

This means, if you dump the raw data of the string, it does NOT change.

When you read back the string raw data, it would just restore the pointer that points to "002".

hope this helps.



回答2:

Unfortunately you cannot do it so simply thus you are writing only a pointer to std::string and not what string contains. You could write string to binary file this way:

afile.open("user.dat",ios::out|ios::binary);

user person;

person.ID ="001";
person.password ="abc";

int len = person.ID.size();
afile.write(reinterpret_cast<char*>(&len), sizeof(len));
afile.write(const_cast<char*>(person.ID.c_str()), len);

len = person.password.size();
afile.write(reinterpret_cast<char*>(&len), sizeof(len));
afile.write(const_cast<char*>(person.password.c_str()), len);

person.ID ="002";
person.password ="def";

afile.close();

And that way you could read

afile.open("user.dat",ios::in|ios::binary);

afile.read(reinterpret_cast<char*>(&len), sizeof(len));
person.ID.resize(len);
afile.read(const_cast<char*>(person.ID.c_str()), len);

afile.read(reinterpret_cast<char*>(&len), sizeof(len));
person.password.resize(len);
afile.read(const_cast<char*>(person.password.c_str()), len);

cout << person.ID << " " << person.password << endl;


回答3:

You are storing the raw data of a struct that, in fact, contains only pointers. That’s the way std::string uses for storing data in order to make the string resizeable. The only reason your program does print any data is that in the moment of reading, the object person still exists. If you put the reading part of your program into a separate program, you won’t be able to read any data.

To actually store your data using fstream, you can store one string per line, for example:

afile << person.ID << endl;
afile << person.password << endl;

The file user.dat:

001
abc
002
def

And you can read it this way:

afile >> person.ID >> person.password;
while (afile.good())
{
  cout<<person.ID
  <<" "
  <<person.password
  <<endl;
  afile >> person.ID >> person.password;    
}

You should also check Serializing a class which contains a std::string.