user732274
user732274

Reputation: 1069

C++ fstream: how to know size of string when reading?

...as someone may remember, I'm still stuck on C++ strings. Ok, I can write a string to a file using a fstream as follows

outStream.write((char *) s.c_str(), s.size());

When I want to read that string, I can do

inStream.read((char *) s.c_str(), s.size());

Everything works as expected. The problem is: if I change the length of my string after writing it to a file and before reading it again, printing that string won't bring me back my original string but a shorter/longer one. So: if I have to store many strings on a file, how can I know their size when reading it back?

Thanks a lot!

Upvotes: 2

Views: 4578

Answers (4)

Jon Purdy
Jon Purdy

Reputation: 54971

You shouldn’t be using the unformatted I/O functions (read() and write()) if you just want to write ordinary human-readable string data. Generally you only use those functions when you need to read and write compact binary data, which for a beginner is probably unnecessary. You can write ordinary lines of text instead:

std::string text = "This is some test data.";
{
    std::ofstream file("data.txt");
    file << text << '\n';
}

Then read them back with getline():

{
    std::ifstream file("data.txt");
    std::string line;
    std::getline(file, line);
    // line == text
}

You can also use the regular formatting operator >> to read, but when applied to string, it reads tokens (nonwhitespace characters separated by whitespace), not whole lines:

{
    std::ifstream file("data.txt");
    std::vector<std::string> words;
    std::string word;
    while (file >> word) {
        words.push_back(word);
    }
    // words == {"This", "is", "some", "test", "data."}
}

All of the formatted I/O functions automatically handle memory management for you, so there is no need to worry about the length of your strings.

Upvotes: 3

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153810

If you want to use unformatted I/O your only real options are to either use a fixed size or to prepend the size somehow so you know how many characters to read. Otherwise, when using formatted I/O it somewhat depends on what your strings contain: if they can contain all viable characters, you would need to implement some sort of quoting mechanism. In simple cases, where strings consist e.g. of space-free sequence, you can just use formatted I/O and be sure to write a space after each string. If your strings don't contain some character useful as a quote, it is relatively easy to process quotes:

std::istream& quote(std::istream& out) {
    char c;
    if (in >> c && c != '"') {
        in.setstate(std::ios_base::failbit;
    }
}

out << '"' << string << "'";
std::getline(in  >> std::ws >> quote, string, '"');

Obviously, you might want to bundle this functionality a class.

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726489

Although your writing solution is more or less acceptable, your reading solution is fundamentally flawed: it uses the internal storage of your old string as a character buffer for your new string, which is very, very bad (to put it mildly).

You should switch to a formatted way of reading and writing the streams, like this:

Writing:

outStream << s;

Reading:

inStream >> s;

This way you would not need to bother determining the lengths of your strings at all.

This code is different in that it stops at whitespace characters; you can use getline if you want to stop only at \n characters.

Upvotes: 1

perreal
perreal

Reputation: 97928

You can write the strings and write an additional 0 (null terminator) to the file. Then it will be easy to separate strings later. Also, you might want to read and write lines

outfile << string1 << endl;
getline(infile, string2, '\n');

Upvotes: 0

Related Questions