16.1 C File IO

I l @ ve RuBoard

16.1 C++ File I/O

C++ file I/O is based on three classes: the istream class for input, the ostream class for output, and the iostream class for input/output. C++ refers to files as stream s since it considers them a stream of bytes. Four class variables are automatically created when you start a program. These are listed in Table 16-1.

Table 16-1. Predefined I/O class variables

Variable

Use

cin

Console input (standard input)

cout

Console output (standard output)

cerr

Console error (standard error)

clog

Console log

These variables are defined in the standard include file <iostream> . Normally, std::cin is assigned to the keyboard and std::cout , std::cerr ,and std::clog are assigned to the screen. Most operating systems allow you to change these assignments through I/O redirection (see your operating system manual for details).

For example, the command:

 my_prog <file.in 

runs the program my_prog and assigns std::cin to the file file.in.

When doing I/O to disk files (except through redirection), you must use the file version of the stream classes. These are std:: ifstream , std:: ofstream , and std::fstream and are defined in the include file <fstream> .

Suppose you want to read a series of 100 numbers from the file numbers.dat . You start by declaring the input file variable:

 std::ifstream data_file;    // File we are reading the data from 

Next you need to tell C++ what disk file to use. This is done through the open member function:

 data_file.open("numbers.dat"); 

Now you can read the file using the same statements you've been using to read std::cin :

 for (i = 0; i < 100; ++i) {     assert(i >= 0);     assert(i < sizeof(data_array)/sizeof(data_array[0]));     data_file >> data_array[i]; } 

Finally you need to tell the I/O system that you are done with the file:

 data_file.close(  ); 

Closing the file frees resources that can then be used again by the program.

C++ allows the open call to be combined with the constructor. For example, instead of writing:

 std::ifstream data_file;    // File we are reading the data from data_file.open("numbers.dat"); 

you can write:

 std::ifstream data_file("numbers.dat");  // File we are reading the data from 

Additionally, the destructor automatically calls close .

But what if the file numbers.dat is missing? How can you tell if there is a problem? The member function bad returns true if there is a problem, and false otherwise . So to test for problems, all you need is:

 if (data_file.bad(  )) {     std::cerr << "Unable to open numbers.dat\n";     exit (8); } 

A better version of the program for reading numbers is listed in Example 16-1.

Example 16-1. read/read.cpp
 /********************************************************  * read -- read in 100 numbers and sum them             *  *                                                      *  * Usage:                                               *  *      read                                            *  *                                                      *  * Numbers are in the file "numbers.dat"                *  *                                                      *  * Warning: No check is made for a file with less than  *  * 100 numbers in it.                                   *  ********************************************************/ #include <iostream> #include <fstream> #include <cstdlib> int main(  ) {     const int DATA_SIZE = 100;  // Number of items in the data     int data_array[DATA_SIZE];  // The data     std::ifstream data_file("numbers.dat"); // The input file     int i;                      // Loop counter     if (data_file.bad(  )) {         std::cerr << "Error: Could not open numbers.dat\n";         exit (8);     }     for (i = 0; i < DATA_SIZE; ++i) {         assert(i >= 0);         assert(i < sizeof(data_array)/sizeof(data_array[0]));         data_file >> data_array[i];     }     int total;  // Total of the numbers     total = 0;     for (i = 0; i < DATA_SIZE; ++i) {         assert(i >= 0);         assert(i < sizeof(data_array)/sizeof(data_array[0]));         total += data_array[i];     }     std::cout << "Total of all the numbers is " << total << '\n';     return (0); } 

If you want to read a line of data, you need to use the getline function. It is defined as: [1]

[1] If you take a look at the C++ standard, you'll notice that the formal definition of these functions is somewhat more complex. I've simplified the definition for this book, but this definition is compatible with the formal one.

 std::istream& getline(std::istream& input_file,                        std::string& the_string); std::istream& getline(std::istream& input_file,                        std::string& the_string, char delim) 

This function reads a line and stores it in a string. The function returns a reference to the input stream. The second form of the function allows you to specify your own end-of-line delimiter . If this is not specified, it defaults to newline ( '\n' ).

16.1.1 Reading C-Style Strings

To read C-style strings, you can use the getline function. (This is an overload version of the getline function discussed in the previous section.) This getline member function is defined as:

 std::istream& getline(char *buffer, int len, char delim = '\n') 

The parameters to this function are:

buffer

A C-style string in which to store the data that has been read.

len

Length of the buffer in bytes. The function reads up to len-1 bytes of data into the buffer. (One byte is reserved for the terminating null character \0.) This parameter is usually sizeof(buffer) .

delim

The character used to signal end-of-line.

This function returns a reference to the input file. The function reads up to and including the end-of-line character ( '\n' ). The end-of-line character is not stored in the buffer. (An end-of-string ( '\0' ) is store in to terminate the string.)

For example:

 char buffer[30]; std::cin.getline(buffer, sizeof(buffer)); 

16.1.2 Output Files

The functions for output files are similar to input files. For example, the declaration:

 std::ofstream out_file("out.dat"); 

creates a file named out.dat and lets you write to the file using the file variable out_file .

Actually, the constructor can take two additional arguments. The full definition of the output file constructor is:

 std::ofstream::ofstream(const char *name, int mode=std::ios::out,                     int prot = filebuf::openprot); 

The parameters for this function are:

name

The name of the file.

mode

A set of flags ORed together that determine the open mode. The flag std::ios::out is required for output files. Other flags are listed in Table 16-2. (The std::ios:: prefix is used to indicate the scope of the constant. This operator is discussed in more detail in Chapter 21.)

prot

File protection. This is an operating system-dependent value that determines the protection mode for the file. In Unix the protection defaults to 0644 (read/write owner, group read, others read). For MS-DOS/Windows this defaults to 0 (normal file).

Table 16-2. Open flags

Flag

Meaning

std::ios::app

Append data to the end of the output file.

std::ios::ate

Go to the end of the file when opened.

std::ios::in

Open for input (must be supplied to the open member function of std::ifstream variables).

std::ios::out

Open file for output (must be supplied to the open member function of std::ofstream variables).

std::ios::binary

Binary file (if not present, the file is opened as an ASCII file). See the later Section 16.5 for a definition of a binary file.

std::ios::trunc

Discard contents of existing file when opening for write.

std::ios::nocreate

Fail if the file does not exist. (Output files only. Opening an input file always fails if there is no file.)

std::ios::noreplace

Do not overwrite existing file. If a file exists, cause the open to fail.

For example, the statement:

 std::ofstream out_file("data.new", std::ios::outstd::ios::binarystd::ios::nocreate                     std::ios::app); 

appends ( std::ios::app ) binary data ( std::ios::binary ) to an existing file ( std::ios::nocreate ) named data.new .

Example 16-2 contains a short function that writes a message to a log file. The first thing the function does is to open the file for output ( std::ios::out ), appending ( std::ios::app ), with the writing to start at the end of the file ( std::ios::ate ). It then writes the message and closes the file (the destructor for out_file performs the close).

This function was designed to be simple, which it is. But also we didn't care about efficiency, and as a result this function is terribly inefficient. The problem is that we open and close the file every time we call log_message . Opening a file is an expensive operation, and things would go much faster if we opened the file only once and remembered that we had it open in subsequent calls.

Example 16-2. log/log.cpp
 #include <iostream> #include <fstream> void log_message(const string& msg) {     std::ofstream out_file("data.log",      if (out_file.bad(  ))         return; /* Where do we log an error if there is no log */     out_file << msg << endl; } 
I l @ ve RuBoard


Practical C++ Programming
Practical C Programming, 3rd Edition
ISBN: 1565923065
EAN: 2147483647
Year: 2003
Pages: 364

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net