What the Point of Using Std::Ios_Base::Binary

What the point of using std::ios_base::binary?

The differences between binary and text modes are implementation
defined, but only concern the lowest level: they do not change the
meaning of things like << and >> (which insert and extract textual
data). Also, formally, outputting all but a few non-printable
characters (like '\n') is undefined behavior if the file is in text
mode.

For the most common OSs: under Unix, there is no distinction; both are
identical. Under Windows, '\n' internally will be mapped to the two
character sequence CR, LF (0x0D, 0x0A) externally, and 0x1A will be
interpreted as an end of file when reading. In more exotic (and mostly
extinct) OSs, however, they could be represented by entirely different
file types at the OS level, and it could be impossible to read a file in
text mode if it were written in binary mode, and vice versa. Or you
could see something different: extra white space at the end of line, or
no '\n' in binary mode.

With regards to always setting std::ios_base::binary: my policy for
portable files is to decide exactly how I want them formatted, set
binary, and output what I want. Which is often CR, LF, rather than just
LF, since that's the network standard. On the other hand, most
Windows programs have no problems with just LF, but I've encountered
more than a few Unix programs which have problems with CR, LF; which
argues for systematically using just LF (which is easier, too). Doing
things this way means that I get the same results regardless of whether
I'm running under Unix or under Windows.

What is the purpose of using std::ios_base::trunc flag with std::ios_base::out

Yes, std::ios_base::out is equivalent to "w" in fopen.

The point of std::ios_base::trunc is when std::ios_base::in and std::ios_base::out are used the same time.

  • in | out is equivalent to "r+"
  • in | out | trunc is equivalent to "w+"
  • binary | in | out is equivalent to "rb+"
  • binary | in | out | trunc is equivalent to "wb+"

Maybe a table would be more obvious:

binary  in  out  trunc | stdio equivalent
-----------------------+-----------------
+ + | "r+"
+ + + | "w+"
+ + + | "r+b"
+ + + + | "w+b"

std::ios_base::ate and std::ios_base::trunc

std::ios_base::ate position the cursor at the end of the text whereas std::ios_base_app appends text (with a write operation) at the end, though you can still read from the beginning :)

std::ios_base::trunc truncates the file so it is emptied, whereas std::ios_base::out just specify you want to write to the stream.

I currently can't quote the standard (on my tablet and Acrobat Reader won't let met copy) but from paragraph 27.4.2.1.4 from ISO 14882:1998 the information you can see on the link is almost exact: http://cplusplus.com/reference/iostream/ios_base/openmode/

To sum up:

std::ios_base::app = append

Append at the end of the stream by "seek[ing] to end before each write"

std::ios_base::ate = At The End

Open and seek immediately at the end after opening

std::ios_base::binary = binary

Perform operation in binary as opposed to text

std::ios_base::in = input

Open in read mode

std::ios_base::out = output

Open in write mode

std::ios_base::trunc = truncate

Truncate the stream on opening.

These values are just flags, so you can open a stream in read/write binary at the end with :

std::ios_base::in | std::ios_base::out | std::ios_base::ate | std::ios_base::binary

Concerning the way of using those values, it is as you wish. They are declared as public static fields in std::ios_base class (see 27.4.2) thus it is possible to access them using std::ios::ate or even something like cout.binary !


The points where you must take attention is that std::ios_base::ate does NOT imply std::ios_base::app nor does std::ios_base::out implies std::ios_base::trunc. Each field has a different meaning, and a different case of use, though most of them can't be used alone :)

Is there a difference between ifstream::binary and ios::binary?

There's no difference. These names are inherited from the virtual base std::ios_base from which the concrete stream classes derive.

Why it refuses to write the binary of an integer to a file?

Using ios::binary does not cause it to write binary data into a file, it simply disables some of the formatting, see What the point of using std::ios_base::binary? for instance.

If you wish to write an integer to a file as binary, I suggest you use something like this:

int foo = 0x0F00;
ofstream bar("test.txt", ios::out | ios::binary);
bar.write(reinterpret_cast<char*>(&foo), sizeof(int));
bar.close();

This will take the integer, reinterpret it as a character sequence and output it to the file without formatting (ios::binary is needed for this).

Seeing as you've since edited the question: You can not sum like that on the disk, you can only write in bytes, this will overwrite any data that was there before. To do what you want you have to read the number, sum it up and then write it back down. You do that by using foo.read(...) with foo now being fstream and opened with ios::in as well.

Has there been a proposal to add std::bin to the c++ standard?

As far as I know there was no proposal submitted to add a formatting flag to add binary formatting and/or a manipulator std::bin. You can check the proposals at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/. I'm pretty sure the proposal to add binary literals did not add this facility (a quick search revealed N3472 but I'm not sure if this is the latest version of the paper).

From a technical point of view it may not be entirely easy to add! The various flags are normally all stored in just one word in the stream class and there are various reasons to use up all the bits. The existing three settings (std::ios_base::oct, std::ios_base::dec, std::ios_base::hex) can be nicely stored in just 2 bits. Of course, the three values would leave one value open except that this value is typically taken for the default setting, i.e., not fixing the base when reading. As a result it may be necessary to change the layout of the stream classes or to make processing less efficient (e.g., by somehow using an iword() to store the additional possibility of binary formatting). I haven't done the analysis whether there is an actual problem with any of the implementations (I know there is none for my implementation but I did use all the bits in a word if I recall correctly).

If you want to support binary formatting it is relatively easy to add via a custom std::num_put<char> facet. Below is a simple example. It doesn't deal with some of the formatting options which may be desirable like padding or digit separators:

#include <iostream>
#include <limits>
#include <locale>

class binary_num_put
: public std::num_put<char> {
template <typename T>
iter_type common_put(iter_type out, std::ios_base& str, char_type fill,
T original, unsigned long long v) const {
if (str.flags() & std::ios_base::basefield) {
return this->std::num_put<char>::do_put(out, str, fill, original);
}
if (str.flags() & std::ios_base::showbase) {
*out++ = '0';
*out++ = str.flags() & std::ios_base::uppercase? 'B': 'b';
}
unsigned long long mask(1ull << (std::numeric_limits<unsigned long long>::digits - 1));
while (mask && !(mask & v)) {
mask >>= 1;
}
if (mask) {
for (; mask; mask >>= 1) {
*out++ = v & mask? '1': '0';
}
}
else {
*out++ = '0';
}
return out;
}
iter_type do_put(iter_type out, std::ios_base& str, char_type fill, long v) const {
return common_put(out, str, fill, v, static_cast<unsigned long>(v));
}
iter_type do_put(iter_type out, std::ios_base& str, char_type fill, long long v) const {
return common_put(out, str, fill, v, static_cast<unsigned long long>(v));
}
iter_type do_put(iter_type out, std::ios_base& str, char_type fill, unsigned long v) const {
return common_put(out, str, fill, v, v);
}
iter_type do_put(iter_type out, std::ios_base& str, char_type fill, unsigned long long v) const {
return common_put(out, str, fill, v, v);
}
};

std::ostream& bin(std::ostream& out) {
auto const& facet = std::use_facet<std::num_get<char>>(out.getloc());
if (!dynamic_cast<binary_num_put const*>(&facet)) {
std::locale loc(std::locale(), new binary_num_put);
out.imbue(loc);
}
out.setf(std::ios_base::fmtflags(), std::ios_base::basefield);
return out;
}

int main()
{
std::cout << std::showbase << bin << 12345 << " "
<< std::dec << 12345 << "\n";
}

Should I read and write from the same file?

Yes, std::fstream supports both reading and writing using the same file stream object. Since you initially didn't show your code, I could only guess that you are not setting the file position pointer properly. When using seekg() or seekp(), keep in mind that in case of file streams, these both refer to the same position pointer. (For std::stringstream, these are different, but not for std::fstream.)

To create a file (as opposed to just opening an existing one), you need to specify the trunc mode. Also, you need both in and out modes (for reading and writing). And since you are dealing with binary data (not text), open the file in binary mode. In other words, do f.open("hello.txt", std::ios_base::binary | std::ios_base::trunc | std::ios_base::in | std::ios_base::out);.

Also, when printing the contents of the file, you need to cast b[i] to an integer, because the << operator overload used in std::cout for a char argument will try to print a character, not its numerical value. In other words, do std::cout << static_cast<int>(b[i]) << '\n';.

Include of iostream leads to different binary

Each translation unit that includes <iostream> contains a copy of ios_base::Init object:

static ios_base::Init __ioinit;

This object is used to initialize the standard streams (std::cout and its friends). This method is called Schwarz Counter and it ensures that the standard streams are always initialized before their first use (provided iostream header has been included).

That function _GLOBAL__sub_I_main is code the compiler generates for each translation unit that calls the constructors of global objects in that translation unit and also arranges for the corresponding destructor calls to be invoked at exit. This code is invoked by the C++ standard library start-up code before main is called.

skipws flag set when opening an input file stream in binary mode

"Binary" mode, as controlled by std::ios_base::binary is only for switching off the translation of newlines between the standard C++ \n character and the system specific newline sequence as stored in files.

It's completely independent of whether you are parsing a file that contains meaningful separating whitespace or some completely different byte format so there's no reason to tie the two orthogonal pieces of functionality together.

(The C++ standard doesn't say much about what binary mode means, there is more detail in the C standard which talks about the potential differences between text streams and binary streams. Binary streams must read back byte for byte as they were written on any given system whereas text stream need only do so given a number of restrictions centring around not having extra spaces before a newline and not having any control characters other than newlines and tabs. A system need not make any distinction at all between binary and text streams.)



Related Topics



Leave a reply



Submit