Cpp Chapter 17: Input, Output, and Files Part1
17.1 An Overview of C++ Input and Output
Unlike other languages that place I/O in keywords, C and C++ leaves I/O purely to implementers. C++'s solution is a set of classes defined in the iostream
and fstream
header files. ANSI/ISO C++ committee decided to formalize this library as a standard class library and goes with the C++.
) Streams and Buffers
A C++ program views input or output as a stream of bytes. A stream acts as an intermediary between the program and the stream's destination.
Managing input involves two stages:
1 Associating a stream with an input to a program(program-end)
2 Connecting the stream to a file(file-end)
Usually, input and output could be handled more efficiently by using buffer. A buffer is a chunk of memory used as an intermediate and temporary storage for the transfer of information. For example, when you read data from a hard disk, you could first read a chunk of data from the hard disk to the buffer, then let the program read the buffer byte by byte. This is faster due to the fact that programs reads things faster from memory then from hard disk. Similarly, on output you could also fill the buffer and transfer the buffer to the hard disk, and flush the buffer for next output. A C++ program usually flushes the buffer when you press Enter, that's why the program only starts executing when you press Enter after requesting for input.
) Streams, Buffers, and the iostream
File
Including the iostream
file brings in several classes designed to implement streams and buffers for you:
1 streambuf
class provides memory for buffers and implements the class methods
2 ios_base
class represents general properties of a stream
3 ostream
class inherits from ios_base
class and provides output methods
4 istream
class inherits from ios_base
class and provides input methods
5 iostream
class inherits both from ostream
and istream
classes and implement both methods
Creating an object of such classes opens a stream, automatically creates a buffer and associates it with the stream.
Including the iostream
file automatically creates 8 stream objects(1 for narrow character and 1 for wide character):
1 cin
object correponds to standard input stream, associated with standard input device(keyboard)
2 cout
object corresponds to standard output stream, associated with standard output device(monitor)
3 cerr
object corresponds to standard error stream, which is unbuffered
4 clog
object corresponds to standard error stream, which is buffered
For example, a statement like cout << "thing";
would place the characters in "thing" into the buffer managed by cout
via the streambuf
object.
) Redirection
Many operating systems support redirection, which lets you change the associations for the standard input and standard output. For example, you may run an executable program(counter.exe) in command line like this:
C>counter
...
C>
With input redirection(<) and output redirection(>), you can let the program read from a file and print to a file:
C>counter <in >out
This lets the program read from file in
and print to file out
, which associates the standard input with the in
file, and associates the standard output with the out
file.
17.2 Output with cout
One of the most important tasks of the ostream
class is to convert numeric types into a stream of characters.
) The Overloaded << operator
The ostream
class redefines <<
, which was originally the bitwise left-shift operator, into insertion operator to handle output for ostream
class. It has overloaded version for all the basic types of C++, four forms of char
pointers and allows output concatenation by making the overloaded function return ostream &
.
) Other output methods
The put()
function had such prototype:
ostream & put(char);
You could give it both a char and an int for it to convert to char and print it:
cout.put('W');
cout.put(55);
You could also concatenate the put()
function:
cout.put('i').put('u');
The write()
method accepts two arguments, first being the address of string to be displayed, the second being how many characters to display.
Here comes code illustrating the use of write()
:
// write.cpp -- using cout.write()
#include <iostream>
#include <cstring>
int main()
{
using std::cout;
using std::endl;
const char * state1 = "Florida";
const char * state2 = "Kansas";
const char * state3 = "Euphoria";
int len = std::strlen(state2);
cout << "Increasing loop index:\n";
int i;
for (i = 1; i < len; i++)
{
cout.write(state2, i);
cout << endl;
}
cout << "Decreasing loop index:\n";
for (i = len; i > 0; i--)
cout.write(state2, i) << endl;
cout << "Exceeding string length:\n";
cout.write(state2, len + 5) << endl;
return 0;
}
Noteworthy, the write()
method doesn't stop printing characters automatically when it came across the null character, so it would keep on reading and printing even it is out of the string's boundary.
You could also print numeric types with write()
:
long val = 560031834;
cout.write( (char *) & val, sizeof(long));
It doesn't translate a number into correct characters, however, it transmits the bit representation as stored in the memory.
) Flushing the Output buffer
ostream
class has a buffer, meaning that output isn't sent to destination unless the buffer is filled and got flushed. This increases productivity sometimes, but it would be troublesome in screen output. Fortunately, you could use a newline character as a sign for ostream
class to flush the buffer and deliver the output. You could also use manipulators to force flushing:
cout << "hi" << flush;
cout << "ei" << endl;
The flush
manipulator flushes the output buffer and the endl
manipulator flushes while adding a newline character to it. Manipulators are indeed functions.
) Formatting with cout
The ostream operator handles output like this on default:
1 A type char or string is displayed in a field equal in width to itself
2 A numeric integer are displayed as decimal integers just right to hold the number and a minus sign if necessary. Floating point types are displayed with a total of six digits, trailing zeros aren't displayed. The number is displayed in fixed-point or E notation according to its size(exponent 6 larger or -5 smaller would use E notation)
Here comes a program which shows the default implementation for output format:
// defaults.cpp -- cout default formats
#include <iostream>
int main()
{
using std::cout;
cout << "12345678901234567890\n";
char ch = 'K';
int t = 273;
cout << ch << ":\n";
cout << t << ":\n";
cout << -t << ":\n";
double f1 = 1.200;
cout << f1 << ":\n";
cout << (f1 + 1.0 / 9.0) << ":\n";
double f2 = 1.67E2;
cout << f2 << ":\n";
f2 += 1.0 / 9.0;
cout << f2 << ":\n";
cout << (f2 * 1.0e4) << ":\n";
double f3 = 2.3e-4;
cout << f3 << ":\n";
cout << f3 / 10 << ":\n";
return 0;
}
) Changing the number base used for display
You may use manipulators hex
dec
oct
for changing the number base. Syntax:
cout << hex;
The manipulators are in the std
namespace, here comes code illustrating the usage of these manipulators:
// manip.cpp -- using format manipulators
#include <iostream>
int main()
{
using namespace std;
cout << "Enter an integer: ";
int n;
cin >> n;
cout << "n n*n\n";
cout << n << " " << n * n << " (decimal)\n";
cout << hex;
cout << n << " " << n * n << " (hexadecimal)\n";
cout << oct;
cout << n << " " << n * n << " (octal)\n";
dec(cout); // another syntax
cout << n << " " << n * n << " (decimal)\n";
return 0;
}
) Adjusting field widths
The width()
method only affects the next item displayed, and the field with reverts to default after the code. Also it uses right-justification, meaning that it there are surplus spaces, they would be on the left of the field while the contents on the right of the field. C++ also expands the field when necessary, to prevent truncation. The usage of width()
is:
cout.width(12);
cout << 12;
cout << 185;
The result would be 12185
, here comes code illustrating the feature of the width()
function:
// width.cpp -- using the width method
#include <iostream>
int main()
{
using std::cout;
int w = cout.width(30); // save previous width in w
cout << "default field width = " << w << ":\n";
cout.width(5);
cout << "N" << ":";
cout.width(8);
cout << "N * n" << ":\n";
for (long i = 1; i <= 100; i *= 10)
{
cout.width(5);
cout << i << ":";
cout.width(8);
cout << i * i << ":\n";
}
return 0;
}
The character used to fill the empty field is called fill characters, which is by default space.
) Fill characters
You could use the fill()
method to change the fill characters:
cout.fill('*');
Here comes code illustrating this feature:
// fill.cpp -- changing fill character for fields
#include <iostream>
int main()
{
using std::cout;
cout.fill('*');
const char * staff[2] = {"Waldo Whipsnade", "Wilmarie Wooper"};
long bonus[2] = {900, 1350};
for (int i = 0; i < 2; i++)
{
cout << staff[i] << ": $";
cout.width(7);
cout << bonus[i] << "\n";
}
return 0;
}
) Setting floating-point display precision
In default mode, precision
means the total number of digits displayed. In fixed and scientific mode, precision
means the number of digits to the right of the decimal point. You could use the precision()
function to alter it:
cout.precision(2);
The effect lasts until it is reset. Here comes code illustrating this feature:
// precise.cpp -- setting the precision
#include <iostream>
int main()
{
using std::cout;
float price1 = 20.40;
float price2 = 1.9 + 8.0 / 9.0;
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Fiends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Fiends\" is $" << price2 << "!\n";
return 0;
}
) Printing trailing zeros and decimal points
The ios_base
provides a setf()
to accomplish this. It also defines constants representing whether showing decimal points and trailing zeros or not. You could use this code to display trailing zeros, and just like precision()
, the state is maintained unless you change it again.
Here comes code illustrating the feature of setf()
:
// showpt.cpp -- setting the precision, showing trailing point
#include <iostream>
int main()
{
using std::cout;
using std::ios_base;
float price1 = 20.40;
float price2 = 1.9 + 8.0 / 9.0;
cout.setf(ios_base::showpoint);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Fiends\" is $" << price2 << "!\n";
cout.precision(2);
cout << "\"Furry Friends\" is $" << price1 << "!\n";
cout << "\"Fiery Fiends\" is $" << price2 << "!\n";
return 0;
}
) More about setf()
The ios_base
class has a protected data member in which individual bits control different formatting aspects, which could be altered by setf()
. The function has two prototypes, the first one is:
fmtflags setf(fmtflags);
fmtflags is a bitmask
type name, which used to hold the format flags. The ios_base
class defines several constants that represent bit values:
Constant | Meaning |
---|---|
ios_base::boolalpha | input and output bool value as true and false |
ios_base::showbase | Use C++ base prefix(O, Ox) on output |
ios_base::showpoint | Show trailing decimal point |
ios_base::uppercase | Use uppercase letters for hex output |
ios_base::showpos | Use + before positive numbers |
These constants are defined within the ios_base
class, you must use the scope-resolution operator with them. Here comes code illustrating these formatting settings:
// setf.cpp -- using setf() to control formatting
#include <iostream>
int main()
{
using std::cout;
using std::endl;
using std::ios_base;
int temperature = 63;
cout << "Today's water temperature: ";
cout.setf(ios_base::showpos);
cout << temperature << endl;
cout << "For our programming friends, that's\n";
cout << std::hex << temperature << endl;
cout.setf(ios_base::uppercase);
cout.setf(ios_base::showbase);
cout << "or\n";
cout << temperature << endl;
cout << "How " << true << "! oops -- How ";
cout.setf(ios_base::boolalpha);
cout << true << "!\n";
return 0;
}
The second setf()
prototype takes two arguments:
fmtflags setf(fmtflags, fmtflags);
The first argument is a value that contains the desired setting. The second argument is a value that first clears the corresponding bits. Here are some matches for the first and second arguments:
Second Argument | First Argument | Meaning |
---|---|---|
ios_base::basefield | ios_base::dec | use base 10 |
ios_base::oct | use base 8 | |
ios_base::hex | use base 16 | |
ios_base::floatfield | ios_base::fixed | use fixed-point notation |
ios_base::scientific | use scientific notation | |
ios_base::adjustfield | ios_base::left | use left-justification |
ios_base::right | use right-justification | |
ios_base::internal | left sign or base prefix, right value |
Fixed-point notation means to use 123.4
style, regradless of the size of number, and scientific notation means to use 1.23e04
style, regardless of the size of number. You could invoke the setf()
function with an cout
object. Here comes code illustrating the second prototype of setf()
:
// setf2.cpp -- using setf() with 2 arguments to control formatting
#include <iostream>
#include <cmath>
int main()
{
using namespace std;
cout.setf(ios_base::left, ios_base::adjustfield);
cout.setf(ios_base::showpos);
cout.setf(ios_base::showpoint);
cout.precision(3);
ios_base::fmtflags old = cout.setf(ios_base::scientific, ios_base::floatfield);
cout << "Left Justification:\n";
long n;
for (n = 1; n <= 41; n += 10)
{
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << endl;
}
cout.setf(ios_base::internal, ios_base::floatfield);
cout.setf(old, ios_base::floatfield);
cout <<"Internal Justification:\n";
for (n = 1; n <= 41; n += 10)
{
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
cout.setf(ios_base::right, ios_base::adjustfield);
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << "Right Justification:\n";
for (n = 1; n <= 41; n += 10)
{
cout.width(4);
cout << n << "|";
cout.width(12);
cout << sqrt(double(n)) << "|\n";
}
return 0;
}
There is also a unsetf()
function that turns off the flag:
void unsetf(fmtflags mask);
This code makes output bool variable display 0 and 1:
cout.unsetf(ios_base::boolalpha);
You could also get flags to default mode in the following ways:
cout.setf(0, ios_base::floatfield);
cout.unsetf(ios_base::floatfield);
) Standard Manipulators
In order to save from the trouble of setf()
, C++ provides some standard manipulators, which could be just used as the hex
and oct
mentioned before.
boolalpha
noboolalpha
showbase
noshowbase
showpoint
noshowpoint
showpos
noshowpos
uppercase
nouppercase
internal
left
right
dec
hex
oct
fixed
scientific
C++ also provides iomanip
header file which supports setprecision()
setfill()
and setw()
, which would be illustrated in the following code:
// iomanip.cpp -- using manipulators from iomanip
#include <iostream>
#include <iomanip>
#include <cmath>
int main()
{
using namespace std;
cout << fixed << right;
cout << setw(6) << "N" << setw(14) << "square root" << setw(15) << "fourth root\n";
double root;
for (int n = 10; n <= 100; n += 10)
{
root = sqrt(double(n));
cout << setw(6) << setfill('.') << n << setfill(' ') << setw(12) << setprecision(3) << root
<< setw(14) << setprecision(4) << sqrt(root) << endl;
}
return 0;
}