tutorials.neonphog.com icon

c++ - MyCopy - C Style

Introduction Get the Source Compiling and Running It Walkthrough Conclusion

Introduction

Ignoring for a moment the age old argument of whether people prefer the old C-Style file access functions, or should always use the newer c++ classes, the fact of the matter is that you will eventually run into code using C-Style functions, and you should know what it is doing.

Get the Source

mycopy_c.h [view]

mycopy_c.c [view]

Compiling and Running It

1) Compile

Just for fun, let us use the actual c compiler (gcc) instead of g++. Compile the source code with the following commands:

gcc -c -o mycopy_cpp.o mycopy_cpp.cpp
gcc -o mycopy_cpp mycopy_cpp.o

2) Run It

The way to run this file (as it should tell you if you run it without arguments) is to give it first the source file to copy from, then the filename to copy to.

Assuming you have a file "test.txt" (and you have permission to write to a new file in this directory) you should see output like the following:

$ ./mycopy_c test.txt newfile.txt
Copying test.txt to newfile.txt.
$

Walkthrough

1) Includes

#include <stdio.h>

"stdio.h" includes all the functions that we will need to write this program, namely: "printf", "fopen", "fread", "fwrite", and "fclose".

2) Buffer Size

#define BUFSIZE (1024)
We set this to 1 KiB because it is a decently large size that shouldn't slow down our reading by doing lots of little calls.

We will need a memory buffer to hold data as we are transferring it between the files. This preprocessor definition will hold the size of this buffer for use later in our code.

3) The "mycopy_c" function

Now let us skip down to the actual work-horse function: mycopy_cpp

int mycopy_c( const char *sSrc, const char *sDest )

We will take two character strings, the first representing the source filename, and the second, the destination. We will return an exit code.

4) Our Memory Buffer

	char buf[BUFSIZE];

This is our memory buffer. Note the size of it is defined by "BUFSIZE".

5) Getting access to the files

	FILE *fhIn, *fhOut;
	size_t iSize;

When we open a file, the type it returns is a "FILE *", typically referred to as a file handle. We will need one for our source file (fhIn) and one for the destination file (fhOut). the "fread" and "fwrite" functions return the number of bytes they dealt with in the data type of "size_t".

6) Opening and Checking Files

	fhIn = fopen( sSrc, "rb" );
	if( !fhIn )
	{
		printf( "mycopy_c: Error reading from \"%s\".\n", sSrc );
		return 2;
	}
The GNU man page on fopen has the following to say about the "b" flag: "[The letter 'b'] is strictly for compatibility with C89 and has no effect; the 'b' is ignored on all POSIX conforming systems, including Linux. (Other systems may treat text files and binary files differently, and adding the 'b' may be a good idea if you do I/O to a binary file and expect that your program may be ported to non-Unix environments.)"

"fopen" takes a character string representing the filename to open, and a string indicating the mode in which to open the file. "r" indicates read, "w" for write, and "a" for append. "b" stands for binary, and helps ensure consistency when compiling on non-posix systems.

If "fopen" returns 0 (NULL), then we were not successful in opening the file, we should report to the user and exit with an error code.

	fhOut = fopen( sDest, "wb" );
	if( !fhOut )
	{
		printf( "mycopy_c: Error writing to \"%s\".\n", sDest );
		fclose( fhIn );
		return 3;
	}

If there is an error opening the destination file, we have the additional requirement of cleaning up the source (fhIn) filehandle that *was* opened successfully.

6) The actual copy

	iSize = fread( buf, sizeof( buf[0] ), BUFSIZE, fhIn );
	while( iSize > 0 )
	{
		fwrite( buf, sizeof( buf[0] ), iSize, fhOut );
		iSize = fread( buf, sizeof( buf[0] ), BUFSIZE, fhIn );
	}
To make this code more bullet-proof, you should check the output of fwrite to see if it actually wrote "iSize" elements. You should also check "ferror(fhIn)" upon receiving an "iSize" of zero, to see if there was an error (as opposed to end-of-file) reading from the source file.

The "fread" and "fwrite" functions have a possibly unexpected "size" parameter for the purposes of reading and writing discreet chunks of data (i.e. raw struct data). As we are dealing with raw data, we set this to the size of a single character in our memory buffer "sizeof( buf[0] )".

So we pass these functions our memory buffer (buf), the size of one "element", the number of "elements" up to which the function will read/write (BUFSIZE), and the file handle to read or write from/to.

"fread" returns the number of elements actually read into "iSize". We loop until fread returns zero, immediately writing the data in our buffer to fhOut.

7) Cleaning Up

	fclose( fhIn );
	fclose( fhOut );

"fclose" will clean up our file handles.

Conclusion

You should now be able to read and write binary file data utilizing the C-Style file access functions.

tutorials home c++ home