headers:
| Template Class |
Character-based Class |
Wide-Character-based Class |
| basic_streambuf
|
streambuf
|
wstreambuf
|
| basic_ios
|
ios
|
wios
|
| basic_istream
|
istream
|
wistream
|
| basic_ostream
|
ostream
|
wostream
|
| basic_iostream
|
iostream
|
wiostream
|
| basic_fstream
|
fstream
|
wfstream
|
| basic_ifstream
|
ifstream
|
wifstream
|
| basic_ofstream
|
ofstream
|
wofstream
|
Table 12-1: C++ I/O headers.
Overloading I/O Operators
Throughout this course, we have worked with the input and output operators: << and >>. Respectively, they are known as the inserter operator and extractor operators. Even though they are already an overloaded operator (they overload the bit shift operators), inserters and extractors can be again overloaded.
Inserters
It is possible to create an output stream that can be used in any situation. Let's take a look at the setup for an overloaded << operator that outputs three variables of a class type samp_class:
ostream &operator<<(ostream &stream, samp_class o1) { stream ><< o1.a << ", ";
stream << o1.b << ", ";
stream << o1.c << ", ";
return stream;
}
|
This configuration removes the dependency on the limited cout statement and allows for any data type to be output using the << operator.
Extractors
Extractors operate the same way. Let's look at what the code would look like for an overloaded extraction operator that asks for the input of the above three variables:
istream &operator>>(istream &stream, samp_class &o1)
{
cout << "Enter values for a, b, and c: ";
stream >> o1.a >> o1.b >> o1.c;
return stream;
}
|
It is important to note that both inserters and extractors cannot be members of the classes on which they operate. They must either be independent functions or "friends" of those classes.
Formatted I/O
All of the output that we have produced in this course so far has been done so according to the defaults of the C++ I/O system. However, it is also possible to format text and numbers using member functions of the ios class.
Formatting with the ios Member Functions
The ios class contains an enumeration of format flags called fmtflags. The following values are defined in the enumeration:
| adjustfield
|
oct
|
| basefield
|
right
|
| boolalpha
|
scientific
|
| dec
|
showbase
|
| fixed
|
showpoint
|
| floatfield
|
showpos
|
| hex
|
skipws
|
| internal
|
unitbuf
|
| left
|
uppercase
|
Table 12-2: ios member functions.
Let's take a look at the effects that these flags will have on a program's output. When the left flag is active, output is left-justified. When the right flag is set, output is right-justified. The default for output is right-justification for text and left-justified for numbers.
Default numeric values are output in decimal form. This standard can be changed using the hex (hexadecimal) flag or the oct (octal) flags. To return output to decimal form after a change, the dec flag is used. When the showbase flag is on, the base is indicated with a 0x for hexadecimal, or 0 for octal.
The showpos flag causes a plus sign to be displayed before all positive numbers. The showpoint flag causes a decimal point and any trailing zeros to be output for all floating point numbers. The scientific flag, when activated, causes floating-point numbers to be output in scientific notation.
The fixed flag causes floating-point values to be displayed normally. If the boolalpha flag is enabled, Boolean values will be indicated using true and false rather than 1 and 0.
The following program illustrates how these various flags are turned on and off:
#include
using namespace std;
int main()
{
cout.setf(ios::scientific);
cout.setf(ios::showpos);
cout << 8 << " " << 88.88 << '\n';
cout.unsetf(ios::scientific);
cout.unsetf(ios::showpos);
cout << 8 << " " << 88.88;
return 0;
}
|
The output from this program looks like this:
+8 +8.888000e+01
8 88.88
Using I/O Manipulators
There is a large set of functions contained within the header than can be used to alter the output format parameters of a stream. Review the following table of manipulators:
| Manipulator |
Purpose |
Works with Input or Output |
| boolalpha
|
Turns on boolalpha flag
|
Input/Output
|
| dec
|
Turns on dec flag
|
Input/Output
|
| endl
|
Output a newline character and flush the stream
|
Output
|
| ends
|
Output a null
|
Output
|
| fixed
|
Turns on fixed flag
|
Output
|
| flush
|
Flush a stream
|
Output
|
| hex
|
Turns on hex flag
|
Input/Output
|
| internal
|
Turns on internal flag
|
Output
|
| left
|
Turns on left flag
|
Output
|
| noboolalpha
|
Turns off boolalpha flag
|
Input/Output
|
| noshowbase
|
Turns off showbase flag
|
Output
|
| noshowpoint
|
Turns off showpoint flag
|
Output
|
| nowshowpos
|
Turns off showpos flag
|
Output
|
| noskipws
|
Turns off skipws flag
|
Input
|
| nounitbuf
|
Turns off
|
Output
|
| nouppercase
|
Turns off uppercase flag
|
Output
|
| oct
|
Turns on oct flag
|
Input/Output
|
| resetiosflags (fmtflags f)
|
Turn off the flags specified in f
|
Input/Output
|
| right
|
Turns on right flag
|
Output
|
| scientific
|
Turns on scientific flag
|
Output
|
| setbase(int base)
|
Set the number base to base
|
Input/Output
|
| setfill(int ch)
|
Set the fill character to ch
|
Output
|
| setisosflag(fmtflags f)
|
Turn on the flags specified in f
|
Input/Output
|
| setprecision(int p)
|
Set the number of digits of precision
|
Output
|
| setw(int w)
|
Set the field width to w
|
Output
|
| showbase
|
Turns on showbase flag
|
Output
|
| showpoint
|
Turns on showpoint flag
|
Output
|
| showpos
|
Turns on showpos flag
|
Output
|
| skipws
|
Turns on skipws flag
|
Input
|
| unitbuf
|
Turns on unitbuf flag
|
Output
|
| uppercase
|
Turns on uppercase flag
|
Output
|
| ws
|
Skip leading white space
|
Input
|
Table 12-3: I/O manipulators.
These manipulators are used within the stream. Instead of calling the setf member of the stream object, you can use:
cout << scientific << showpos << 8 << " " << 88.88 << '\n';
for the same effect.
File I/O
The C++ I/O system can also be used to perform file I/O -- to write to and read from files. The header fstream must be used to input or output files.
Opening and Closing Files
To open a file, you must link the file to a stream. The three stream types are input, output, and input/output. To link these streams to a file, the stream must be declared to be of class ifstream, ofstream or fstream for input, output, or input/output respectively. The following code declares three streams, one of each type:
ifstream input_stream;
ofstream output_stream;
fstream io_stream;
To open and output a file named sampfile during initialization, the following would be used:
ofstream output_stream("sampfile");
or
ofstream output_stream;
output_stream.open("sampfile");
The ifstream, ofstream, and fstream classes have constructor functions that automatically open a file once it is linked to a stream. To close the file above, you would use the following code:
output_stream.close();
These classes require no parameters in their declarations, and their close() functions will return no value.
Reading and Writing Text Files
An easy way to read from and write to a text file is by using the << and >> operators. We will now look at two sample programs. The first program writes to a file and the second example reads from that same file.
#include
#include
using namespace std;
int main()
{
ofstream opstream("sampfile");
if(!opstream) {
cout << "File cannot be opened.\n";
return 1;
}
opstream << 3.1415 << " " << 23 << '\n' << "Test stream.";
opstream.close();
return 0;
}
|
Now that we have written to sampfile, let's read from it:
#include
#include
using namespace std;
int main()
{
int i;
float f;
char ch;
char str[25];
ifstream ipstream("sampfile");
if(!ipstream) {
cout << "File cannot be opened.";
return 1;
}
ipstream >> f;
ipstream >> i;
ipstream >> ch;
ipstream >> str;
cout << i << " " << f << " " << ch << " " << str;
ipstream.close();
return 0;
}
|
Your output should be:
3.1415 23
Test stream.
C++ has smart operators. If you had read the variables from the file in a slightly different order:
ipstream >> i;
ipstream >> f;
ipstream >> ch;
ipstream >> str;
the output would have been:
3 0.1415 2 3
C++ will do whatever it can to read the types that you have specified. If it can't find anything to fill the variable, it will error.
Other I/O Considerations
We will now explore member functions and other topics relating to I/O.
EOF
The member function eof() is used to determine when the eofbit has been set, which means that an input stream has reached the end of a file. The following program will display on the screen the contents of the file sampfile:
#include
#include
using namespace std;
int main()
{
char ch;
ifstream ipstream("sampfile", ios::in | ios::binary);
if(!ipstream) {
cout << "File cannot be opened.";
return 1;
}
while(!ipstream.eof()) {
ipstream.get(ch);
// check for eof()
if(!ipstream.eof()) cout << ch;
}
ipstream.close();
return 0;
}
|
The while condition in this example reads "while we have not reached the end of the file ipstream." Your output will be every character in that file.
Another function that works in the same manner is fail(). It will not only stop reading at the end of the file, but if the file fails for any other reason.
Note: This program will read sampfile as unformatted binary. Unformatted binary and binary I/O functions are addressed in great detail in your textbook.
Random Access
Many times, you do not want to read and write data to a file sequentially. Applications that store data to disk, like databases or spreadsheets, need to be able to read and write from anywhere in the file so that data can be updated and queried. We call this functionality "random access."
There are two functions that allow for random, rather than sequential, access to a file. These functions are seekg() and seekp(). The seekg() function moves the get pointer of the referenced file, while seekp() moves the put pointer of a file. These functions take an integer argument and move the read or write pointer to that record location in a file -- it is not a byte location. If the records in the file are larger, the argument acts as a multiplier.
Note: If you read to the end of a file, a failbit will be set and you will not be able to search again until it is cleared.
Checking I/O Status
The ios class defines a type, iostate, which is an enumeration. This enumeration includes members that relate to the status of an I/O stream.
| Name |
Meaning |
| ios::goodbit
|
No error bits set
|
| ios::eofbit
|
1 when end-of-file is encountered; 0 otherwise
|
| ios::failbit
|
1 when a (possibly) nonfatal I/O error has occurred; 0 otherwise
|
| ios::badbit
|
1 when a fatal I/O error has occurred; 0 otherwise
|
Table 12-4: iostate Members Table.
To check the current status of a stream, you can use the rdstate() function. This function is also a member of the class ios:
iostate rdstate();
The above code will return one of the values from the table. If there are no problems, goodbit will be returned. Otherwise, one of the problem members of the enumeration will be returned.
Using C++'s input and output system can be very easy and powerful. However there are some things you need to be aware of, such as what input functions may throw and exception or set error flags. You should consult your compiler's reference as well as the textbook on these matters.
Array-Based I/O
In addition to console and file I/O, C++ allows for I/O to and from arrays using streams. A character array can serve as the input device, output device, or both. Everything that applies to regular C++ I/O streams also applies to array-based I/O. The only difference is that an array of characters, rather than an object, is linked to the stream. In order to use array-based I/O, the header must be included in your program.
Note: Array-based input and output has been deprecated by the latest standards. While it is still applicable its days are numbered. String-based streams are available for future use and are explained as well.
Array-Based Classes
The following are the array-based I/O classes used to create input, output, and input/output streams respectively: istrstream, ostrstream, strstream. These classes are derived from the classes in ios and have access to any of the similar class ( istream, ostream ) ios member functions.
Creating an Array-Based Output Stream
Based on our previous studies of arrays and I/O, you should be able to follow the following sample program.
#include
#include
using namespace std;
int main()
{
char str[50];
ostrstream outa(str, sizeof(str));
outa << "This is a test. ";
outa << 623;
outa << 'A' << ' ' << 3.1415 << ends;
cout << str;
return 0;
}
|
The output will look like this:
This is a test. 623A 3.1415
Creating an Array-Based Input Stream
Let's look at our modified sample program:
#include
#include
using namespace std;
int main()
{
char a[] = "Testing 1 2.3";
istrstream ins(a);
int j;
char str[50];
float f;
ins >> str;
ins >> j;
ins >> f;
cout << f << " " << j << " " << str;
return 0;
}
|
The output looks like this:
2.3 1 Testing
String-Based I/O
In addition to the array-based I/O, C++ allows for I/O to and from strings using streams. A string is a C++ type that is described in detail in the C++ Primer Plus book. This string type can serve as the input device, output device, or both. In order to use character- based I/O, the header must be included in your program.
String-Based Stream Classes
The following are the string-based I/O classes used to create input, output, and input/output streams respectively istringstream, ostringstream, stringstream. These classes are derived from the classes in ios and have access to any of the similar class ( istream, ostream )member functions.
Creating an String-Based Output Stream
Compare these to the deprecated array-based classes.
#include
#include
using namespace std;
int main()
{
ostringstream outa;
outa << "This is a test. ";
outa << 623;
outa << 'A' << ' ' << 3.1415 << ends;
cout << outa.str();
return 0;
}
|
The output will look like this:
This is a test. 623A 3.1415
Creating a String-Based Input Stream
Let's look at our modified input sample program:
#include
#include
using namespace std;
int main()
{
istringstream ins("Testing 1 2.3");
int j;
char str[50];
float f;
ins >> str;
ins >> j;
ins >> f;
cout << f << " " << j << " " << str;
return 0;
}
|
The output looks like this:
2.3 1 Testing
Linkage
You will learn a bit about linkage and separate compilation units in this final section of the course.
Separate Compilation Units
Structured languages (of which C++ is one) use modularized sections of code that are separately compiled and linked to form the program. This allows for reuse, and for thorough testing of a piece of code. It also allows for more than one programmer to work on different areas of a project at the same time.
In previous sections, we've learned about the standard functions that are stored in libraries, and pre-compiled into object code. At link time, the linker phase of your compilation will select pieces of code from the library and add these bits of code to create a machine executable code that is your program.
In C++, good program design demands that you write your source code units separately and that your project manager or command-line linker to form a single executable file link them. Structure your program with one header for each class or set of related functions, and one implementation file for that class or function source code. This header and source file combination will form a translation unit. A translation unit includes your source files and headers, as well as all the files that may have been added to it indirectly through the included headers.
C Linkage
If you mix C and C++ sources in a project, the files with a .cpp extension will be compiled as C++, while those with .c extension will be compiled as C. When it comes time to link these compiled sources, name mangling will cause the code to be unresolved and your program will fail to be built. You may enable your C++ compiler by setting a flag on the command line when you compile, or by setting a preference in your project's settings. You can also use an extern C statement in your source files to tell the compiler you did not use name mangling for those specific functions.
extern "C" {
// function prototypes here
}
When the compiler sees those functions in your C++ source code, it knows not to look for name-mangled object code.
The Macintosh "Universal Headers" are wrapped in the extern C declaration, so it is safe to use them in a C++ program without fear of name mangling problems.