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); 
read_size
Size of the data that was read. If this is less than size, an end-of-file or error occurred.

data_ptr
Pointer to a buffer to receive the data being read.

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

size
Number of bytes to be read.

file
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");
        exit(8);
    }
    std::printf("File found\n");
    std::fclose(in_file);
    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.

Simplicity

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:  1   2   3   4   5   6   7   8   9   Next 



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds