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 istreamclasses 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;
}
posted @ 2018-11-27 20:15  Gabriel_Ham  阅读(254)  评论(0编辑  收藏  举报