March 3, 2021
Hot Topics:

C++ File Input/Output

  • By Steve Oualline
  • Send Email »
  • More Articles »

C-Style Binary I/O

Binary I/O is accomplished through two routines: std::fread and std::fwrite. The syntax for std::fread is:

read_size = std::fread(data_ptr, 1, size, file); 
Size of the data that was read. If this is less than size, an end-of-file or error occurred.

Pointer to a buffer to receive the data being read.

The constant 1. (For the reason behind this constant, see the sidebar.)

Number of bytes to be read.

Input file.

Why 1?

If you look at the documentation for std::fread or std::fwrite, you'll see that it the number of bytes to read is specified as two parameters. These two are multiplied together to determine the number of bytes to actually read. For example, to read 12 bytes you could write:

std::fread(in_file, 3, 4, buffer)

The logic behind this system is that if you are reading in an array, you could specify the size of an element (parameter #2) and the number of elements to read (parameter #4). The function would then return the number of elements read, not the number of bytes.

Since every almost every other standard C and C++ function that deals with memory deals with byte sizes, it's much easier to put a 1 in for parameter #2 and a byte size in for #3 and not have to worry about array elements.

For example:

struct { 
        int     width; 
        int     height; 
} rectangle; 
if (std::fread(<static_cast<char *>&rectangle, 1, 
       sizeof(rectangle), in_file) != sizeof(rectangle)) { 
        std::fprintf(stderr, "Unable to read rectangle\n"); 
        exit (8); 

In this example you are reading in the structure rectangle. The & operator makes the structure into a pointer. The cast static_cast<char *> turns &rectangle into the proper parameter type, and the sizeof operator is used to determine how many bytes to read in as well as to check that the read was successful.

std::fwrite has a calling sequence similar to std::fread:
write_size = std::fwrite(data_ptr, 1, size, file); 

No matter what filename you give Example 16-9, std::fopen can't find it. Why?

Example 16-9: fun-file/fun-file.cpp

#include <cstdio>
#include <cstdlib>
int main(  )
    char            name[100];  /* name of the file to use  */
    std::FILE           *in_file;    /* file for input */
    std::printf("Name? ");
    std::fgets(name, sizeof(name), stdin);
    in_file = std::fopen(name, "r");
    if (in_file == NULL) {
        std::fprintf(stderr, "Could not open file\n");
    std::printf("File found\n");
    return (0);

C- Versus C++- Style I/O

Both C- and C++- style I/O have their own features and quirks. In this section we'll discuss some of the differences between these two systems.


Let's say we want to write a simple checkbook program. We need to print an account statement. We need some code to print each line of the account statement (date, check number, payee, and amount).

In C the print statement looks like:

std::printf("%2d/%2d/%02d %4d: %-40s %f6.2\n",
   check.date.month, check.date.day, check.date.year,
   check.number, check.payee, check.amount);

In C++ the print statement is:

std::cout << setw(2) << check.date.month << '/' <<
             setw(2) << check.date.day << '/' <<
             setw(2) << setfill('0') << check.date.year << ' ' <<
             setw(4) << check.number << ':' << 
             setw(40) << setiosflags(std::ios::left) << 
                         check.payee <<
             resetiosflags(std::ios::left) << ' ' <<
             setw(6) << setprecision(2) << 
             setiosflags(std::ios::fixed) << 
             check.amount << 
             setw(0) << '\n';

From this example we can clearly see that the C-style I/O is more compact. It is not clear that compact is better. This author prefers the compact style of the C std::printf functions, while many others prefer the verbosity of the C++ I/O system. Besides if you're C++ programmers, you probably should program in C++ and not bring legacy I/O systems into the mix.

Although it looks like C is more compact, things are not as obvious as they look. A well-designed date class would have its own output operator. Thus we can simplify our C++ code down to:

    std::cout << check.date <<
                 setw(4) << check.number << ':' << 
                 setw(40) << setiosflags(std::ios::left) << 
                             check.payee <<
                 resetiosflags(std::ios::left) << ' ' <<
                 setw(6) << setprecision(2) << 
                 setiosflags(std::ios::fixed) << 
                 check.amount << 
                 setw(0) << '\n';

But this assumes that only the date has an output operator. If we designed our check class correctly, it should have one as well. This means that our code now has been simplified down to:

    std::cout << check << '\n';

Now this doesn't mean that complexity has gone away. It's merely been moved from outside the class to inside it.

This example serves to illustrate one of the key differences between C and C++. In C-style I/O, the information on how to manipulate the data (in this case, how to print it) is contained outside the data itself. In C++ it's possible to put the manipulation code and the data into a single class.

If we are writing out our checkbook information in only one place, the C version may be simpler and easier to work with. So for simple programs, you may want to consider using C-style I/O. But suppose that we wanted to print out the data to a number of places. If we used C-style I/O, we would have to replicate our format code all over the place or create a small function to do the printing. With C++'s classes, we can keep the printing information in one logical place. (As a person who's just had to rewrite all the C-style format statements in a rather large piece of code, I can tell you that putting the formatting information in one place, the object, has some advantages.)

Page 8 of 9

This article was originally published on March 20, 2003

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date