3

I am trying to get strings from cin and than write it to binary file. I've read that writing pure string won't work, so I tried to convert it to char*.

The following code writes it ok (...probably), but only the first 8 chars, so the output in file is incomplete.

std::string nick, ip, port;

std::cout << "IP: ";
std::cin >> ip;
std::cout << "port: ";
std::cin >> port;

ofstream file1("lastServers.bin", ios::out | ios::binary);
if (file1.good())
{
    const char* p_IP = ip.c_str();
    const char* p_PORT = port.c_str();
    int length = sizeof(&p_IP)+sizeof(&p_PORT);
    char* tmp1 = new char[length];

    int index = 0;
    memcpy((tmp1 + index), p_IP, sizeof(&p_IP));
    index = index + sizeof(&p_IP);
    memcpy((tmp1 + index), p_PORT, sizeof(&p_PORT));

    file1.write(tmp1, length);

    file1.close();
    delete[] tmp1;
}

else
{
    std::cout << "file error write" << endl;
}

Thanks in advance for any help :)

Skipper
  • 775
  • 6
  • 17
  • 32
  • 1
    "I've read that writing pure string won't work" - can you clarify that? Where did you read this? – BoBTFish Dec 17 '14 at 10:24
  • I mean, writing just like (string, sizeof(string)) is not correct as I assume – Skipper Dec 17 '14 at 10:26
  • 3
    I don't understand. What do you want to do that `file << ip << port << nick;` does not achieve? Because that appears to be what you are trying to build here. – Wintermute Dec 17 '14 at 10:29
  • `sizeof(&p_IP)` and `sizeof(&p_PORT)` is the size of a pointer, probably 8. – molbdnilo Dec 17 '14 at 10:33
  • 3
    I might add that the results of `file1.good()` aren't well defined here. You want `file1.is_open()` or `! file1.fail()`. Or just `file1`. – James Kanze Dec 17 '14 at 11:10

2 Answers2

6

Your code can be written as

ofstream file1("lastServers.bin", ios::out | ios::binary);
if (file1.good()) {
    file1.write(ip.c_str(), ip.size());
    file1.write(port.c_str(), port.size());
    file1.close();
}
else {
    std::cout << "file error write" << endl;
}

string::c_str() returns a const pointer to the text in the string. string::size() returns the number of characters in the string.

You don't need to concatenate the data before writing to the file, writing one then the other has the same result.

If you wanted to write C type code rather than C++, you can use strlen(p_IP) to get the length of the IP string rather than using sizeof.

The sizeof operator gives you the size of the class instance, i.e. the size of the object BUT the string object's size is never affected by the size of the string it manages.

In C++, objects that manage something (think strings managing characters, containers managing their contents, etc.) usually have a method to determine the size of what they're managing. For std::string and other STL containers that method is size().

Note that writing these strings in this format means you can't tell where one string ends and another one starts. Two options to consider are using a terminating character that you know won't appear in any strings, or writing the length of the string to the file before the text of the string itself. I won't elaborate here as it was not asked in the original question.

Steve Kidd
  • 356
  • 3
  • 10
  • @giorgim Fair question, have added an extra para to raise the issue. – Steve Kidd Dec 17 '14 at 12:03
  • If you want to use null-terminator you can write file1.write(ip.c_str(), ip.size() + 1) because c_str() returns a pointer to a null-terminated string. – Steve Kidd Dec 17 '14 at 12:27
  • I think what you're saying is if you tried to read it all in AS A STRING then you wouldn't see the second string. I agree. Think you need to treat the file as binary and parse it - e.g. read into memory buffer, search buffer for first null-term - assign these chars to first string. Search buffer for second null-term - assign chars that to second string, etc. – Steve Kidd Dec 17 '14 at 12:39
  • It would still work. If the string contains a null-term, which it is allowed to do, then the whole string gets written to the file. "abc"+nullterm+"de" means size() returns 6. But, if your string can contain null-terms that means you CANNOT use it as a your string terminator - that's when you need to choose a different terminator (which you can only do if you know that no string will ever contain that character) or use the other option I mentioned which is to write the string's length to the file (as binary, fixed 4 byte, or fixed 8 byte most likely) followed by the characters in the string. – Steve Kidd Dec 17 '14 at 12:51
2

sizeof returns you the size of the string object in the memory, not the length of the string itself. Specifically, sizeof(&p_IP) returns the size of the pointer to p_IP, which is always 4 bytes on a 32-bit system. Your variable length simply does not compute to the correct value. To get the length of a char*, use strlen.

Anton Poznyakovskiy
  • 2,109
  • 1
  • 20
  • 38
  • 2
    While technically correct, this answer does nothing to advise the "proper" way to solve this problem. And even assuming you really do want a `char*`+length from a `std::string`, you would be better to use `myString.size()` (or `length()`) than `strlen`. – BoBTFish Dec 17 '14 at 10:32
  • I thought the question was why he only gets 8 bytes of his data saved. In the end, there is no need to use `char*`, of course. – Anton Poznyakovskiy Dec 17 '14 at 10:39