Copying a file is a simple but essential file operation that operating systems typically support directly through the user interface or a system utility. Furthermore, users may copy text and binary files. (Text files are a special subclass of binary files, so the copy operation will work with text files even when opened in binary mode.) Programmers can copy text and binary files with character, block, and buffer I/O operations but not line operations. Therefore, the following program demonstrates copying a file using only those I/O units.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string input; // (a)
cout << "Please enter the source file: ";
getline(cin, input);
ifstream in(input, ios::binary); // (b)
if(!in.good()) // (c)
{
cerr << "Unable to open " << input << endl;
exit(1);
}
string output; // (d)
cout << "Please enter the destination file: ";
getline(cin, output);
ofstream out(output, ios::binary); // (e)
if(!out.good()) // (f)
{
cerr << "Unable to open " << output << endl;
exit(1);
}
// file copy code goes here
return 0;
}
mycopy.cpp: opening the files.
Opening and validating the source and destination files is independent of the specific technique - the functions and I/O units - the program uses to copy a file. So, for brevity, the demonstration presents this "preamble" once, marking the insertion location of the various copy examples.
Prompt for and read the name of the input file
Open the input file in binary mode
Test the input stream, ensuring the file opened successfully, ending the program if it didn't
Prompt for and read the name of the output file
Open the output file in binary mode
Test the output stream, ensuring the file opened successfully, ending the program if it didn't
Character I/O
Programmers can complete the copy function started in the previous figure by replacing the highlighted comment with either of the following options.
int c; // (d)
while ((c = in.get()) != EOF) // (e)
out.put(c); // (f)
mycopy.cpp: Two character copy options.
The two illustrated options follow similar patterns using three functions: put and two overloaded versions of get:
ostream& put(char c);
istream& get(char& c);
int get();
The loop reads data one character or byte at a time, storing each character in c
The get function's parameter is passed by reference, making it an INOUT function. The function returns a stream reference, which its conversion operator, operator bool(), converts to a Boolean value that controls the loop.
The put function sends the character to the output stream, copying it to the output file.
The get function returns each character as a non-negative integer, storing it in c.
The grouping parentheses (in red) force the get function call and assignment operation to run first, storing a character in c. Following the assignment, the expression compares the character stored in c to the end-of-file marker, EOF, signaling when the stream reaches the end of the file.
C++ automatically converts between characters and integers without an explicit type cast, allowing the put function to copy the character to the output file.
mycopy.cpp: block copy.
The block I/O operations read and write data as groups of bytes without interpreting their meaning. C++ does not have a data type called "byte," so programmers use char. A block's size is arbitrary but typically reflects the data's size, such as the size of an object. The copy program processes the contents of a file without regard to any data boundaries that might exist, so the program uses a block size that works well with most hardware. The relevant prototypes are:
Defines a read/write buffer for the block I/O functions.
Stores the number of bytes the lastread extracts from the input file.
The initial read call occurs above the loop. The first parameter is the address of block, allowing the function to store and return the data in block, and the second is the requested number of bytes.
The gcount function returns the number of bytes read by the last read or get operation. If the last block read is shorter than 512 bytes, read returns all available data, and gcount returns the number of bytes read. The loop continues until read fails to return any data and gcount returns 0.
The first parameter is the address of block, and the second is the number of bytes to write to the file. The statement uses the variable count rather than the constant value of 512 because the last block read may be partially filled (i.e., it may contain less than 512 bytes).
The second read function call is inside and at the bottom of the loop, preparing the next gcount call in the while-loop test to process the data or end the loop.
Buffer I/O
out << in.rdbuf(); // (a) & (b)
mycopy.cpp: buffer copy.
Unlike the block copy outlined above, buffer copy does utilize the stream's filebuf. The technique illustrated here is generally not useful for most file processing tasks, but it is very compact and efficient for this specialized task - and it's too clever to ignore.
The rdbuf function returns a pointer to the filebuf object aggregated to the in stream object.
The overloaded inserter function, operator<<, provides the looping needed to copy the entire contents of the input file to the output file.