34

I'm new to C++ std::stream and I'm making some tests. I have this simple code:

int i = 10;
char c = 'c';
float f = 30.40f;

std::ofstream out("test.txt", std::ios::binary | std::ios::out);
if(out.is_open())
{
    out<<i<<c<<f;
    out.close();
}

As the stream is opened as std::ios::binary I expect in the test.txt file to have the binary representation of i, c and f, but instead I have 10c30.4.

Can you please tell me what I'm doing wrong?

Mircea Ispas
  • 20,260
  • 32
  • 123
  • 211
  • From http://www.cplusplus.com/reference/fstream/ofstream/ofstream/ binary (binary) Consider stream as binary rather than text. – Mircea Ispas Feb 08 '13 at 07:45
  • 1
    Okay, on to the POSIX definitions of text file streams vs. binary file streams then :) (Hint: why would writing **text** to a binary file result in something else than text?) – sehe Feb 08 '13 at 07:46
  • 1
    @Felics: the standard does not specify what "binary" actually means. In fact on some platforms it has no effect (e.g. on POSIX). – Yakov Galka Feb 08 '13 at 07:47
  • 9
    To be perfectly clear: +1 for this question. I think it is interesting to find that [documentation on this](http://www.cplusplus.com/reference/ios/ios_base/openmode/) is very sketchy. It is apparently "assumed" to be obvious. More people might be confused, which makes this a perfect [SO] candidate. – sehe Feb 08 '13 at 07:48
  • @ybungalobill In common perception binary means "what is in memory". When using FILE* with "wb" flag I have the expected result. – Mircea Ispas Feb 08 '13 at 07:53
  • 3
    @Felics: your last sentence makes no sense. ISO says that opening an `fstream` with `binary|out` is *equivalent* to `fopen` with `"wb"` mode. Now, when you say "I have the expected result" it means that you called `fwrite`, which corresponds to `ostream::write`. But your code here uses `operator <<`, which corresponds to, e.g. `fprintf(out, "%d", i)` for integers. – Yakov Galka Feb 08 '13 at 08:00
  • @Felics but then you'd have used `(f)write`, not `fprintf`. The latter corresponds to `std::ostream` `operator<<` – sehe Feb 08 '13 at 08:01

2 Answers2

18

std::ios::binary promises to not do any line-end conversions on the stream (and some other small behavioral differences with text streams).

You could look at

Here's an example using Boost Spirit Karma (assuming Big-Endian byte ordering):

#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;

int main()
{
    int i = 10;
    char c = 'c';
    float f = 30.40f;

    std::ostringstream oss(std::ios::binary);
    oss << karma::format(
            karma::big_dword << karma::big_word << karma::big_bin_float, 
            i, c, f);

    for (auto ch : oss.str())
        std::cout << std::hex << "0x" << (int) (unsigned char) ch << " ";
    std::cout << "\n";
}

This prints

0x0 0x0 0x0 0xa 0x0 0x63 0x41 0xf3 0x33 0x33 
sehe
  • 374,641
  • 47
  • 450
  • 633
  • I've added a demo based on Karma, see also here: **[http://liveworkspace.org/code/4B5okj](http://liveworkspace.org/code/4B5okj)** – sehe Feb 08 '13 at 08:00
  • That's not quite "end of story", there are various possible transformations that can be performed on text files, including that the data read back may differ from the data written (e.g. trim trailing space), and some file operations such as `fseek` behave differently for each type. – M.M Aug 20 '14 at 20:10
18

In order to write raw binary data you have to use ostream::write. It does not work with the output operators.

Also make sure if you want to read from a binary file not to use operator>> but instead istream::read.

The links also provide examples how you can handle binary data.

So for your example:

int i = 10;
char c = 'c';
float f = 30.40f;

std::ofstream out("test.txt", std::ios::binary | std::ios::out);
if(out.is_open())
{
    out.write(reinterpret_cast<const char*>(&i), sizeof(i));
    out.write(&c, sizeof(c));
    out.write(reinterpret_cast<const char*>(&f), sizeof(f));
    out.close();
}
mistapink
  • 1,926
  • 1
  • 26
  • 37
  • 2
    beware of UB and unportability when doing this. I'd suggest using well tested libraries to handle this for you, and account for endianness (Boost Spirit, Serialization, protobuf etc.) – sehe Feb 08 '13 at 08:02
  • Yes, you are right. But I just wanted to point to an answer with plain C++ without any boost or other libraries. – mistapink Feb 08 '13 at 08:04
  • E.g. the sample technically requires `reinterpret_cast` to not invoke Undefined Behaviour, I think. (PS. I _did_ point to the answer in plain C++ too :)) – sehe Feb 08 '13 at 08:04
  • 2
    @sehe That (= the code not invoking UB) is guaranteed by the standard, actually. The *result* of doing this, now *that* is unspecified. – Konrad Rudolph Feb 08 '13 at 08:10
  • Please read this Q/A http://stackoverflow.com/q/12612488/85371 for a healthy introduction to the risks of reinterpreting object pointers – sehe Feb 08 '13 at 09:19
  • 1
    I think you're missing a `&` or three. And a couple of `sizeof`s. – molbdnilo Feb 08 '13 at 09:28