Alecto
Alecto

Reputation: 10750

fstream produces REALLY weird behavior

When I write a double to a filestream, then write an integer, the integer gets appended to the double as extra digits, and I have no idea why it happens. Could someone please explain this for me? Minimal example:

#include <iostream>
#include <fstream>

int main()
{
    std::fstream s("test.bin", std::fstream::binary | std::fstream::trunc | std::fstream::in | std::fstream::out);
    s << 3.14;
    int n = 36;
    s << n;
    s.seekp(0);
    double d;
    s >> d;
    printf("%f\n", d);
}

What I expect to happen:

What actually happens: The program outputs 3.143600 - I have absolutely no idea why this happens. It makes zero sense. If I change the initial value, say from 3.14 to 18.3204, then it outputs 18.320436. What's happening?

Upvotes: 1

Views: 139

Answers (2)

It writes the value 3.14 to the file (8 bytes)
It writes the value 36 to the file (4 bytes)

This is not what happens. >> and << and friends read and write values in human-readable form.

s << 3.14; writes the digit 3, a full stop, the digit 1, and the digit 4 to the file (4 ASCII characters). s << 36; writes the digit 3, and the digit 6 to the file (2 ASCII characters).

The file then contains 6 ASCII characters: a 3, a full stop, a 1, a 4, a 3, and a 6. Or as any normal person would write it: it contains 3.1436.

s >> d; reads a number, by characters from the file until it finds a character that doesn't look like a number, and then converting the characters it read into a number (the same way they'd be converted if you typed them into cin). It reads 3, full stop, 1, 4, 3, 6, and then produces the number 3.1436.

Upvotes: 2

user4581301
user4581301

Reputation: 33982

When operating on streams, << and >> perform formatted access. Ignoring the behind -the-scenes voodoo, << writes strings and >> reads strings. So

s << 3.14;` 

turns 3.14 into a string and writes the string to the file. Same with s << n;.

You now have a file containing the characters "3.1436".

s >> d;

reads the file as a string looking for separating whitespace or any other character that cannot be transformed into a double value. Since there is nothing separating 3.14 and 36 in the file, 3.1436 is read back in as a single number.

What you need to do is use raw, unformatted reads and writes:

#include <iostream>
#include <fstream>

int main()
{
    std::fstream s("test.bin",
                   std::fstream::binary | std::fstream::trunc |
                       std::fstream::in | std::fstream::out);
    double d = 3.14;
    int n = 36;
    if (s.write((char*) &d, sizeof(d)) &&
        s.write((char*) &n, sizeof(n)))
    { 
        s.seekp(0);
        if (s.read((char*) &d, sizeof(d)))
        { 
            std::cout << d;
        }
        else 
        { 
             std::cerr << "failed to read\n";
        }
    }
    else
    {
         std::cerr << "failed to write\n";
    }
}

Always test the IO to make sure it succeeded.

Also be warned that this isn't all that portable. A file written on one machine is not guaranteed to be readable on another without more care. ints may be different sizes and Beware the endian my son! The order that bytes...

Documentation on write

Documentation on read

Upvotes: 0

Related Questions