Part B - Foundations

Input and Output Examples

Describe the member functions that control standard input and output
Introduce manipulators to format data on input and output objects
Describe the member functions that manage the state of input and output objects

"Designing and implementing a general input/output facility for a programming language is notoriously difficult. ... Nobody has come up with a solution that pleases everyone" (Stroustrup, 1997)

Streams | Output | Input | State | Robust Validation | Summary | Exercises


The compound types that define the structure of the input and output objects introduced in the first chapter include public member functions for controlling input and output.  The compound types and their objects belong to a library module named iostream and are defined in the <iostream> header file.  These public member functions report the state of each objects and provide control for formatting the data passing through these objects. 

This chapter describes the input and output objects, along with the member functions that control state and formatting.  This description demonstrates the role that member functions play and sets the stage for subsequent coding of member functions on our own compound types. 


Streams

Input and output objects operate on streams of data.  Streams are sequences involving indeterminate numbers of characters. 

The input and output objects of the iostream library manage the flow of characters respectively from input streams to memory and from memory to output streams.  These objects represent physical devices, such as keyboards and displays.  The input object extracts data from the input stream attached to it.  The output object inserts data into the output stream attached to it. 

file streams

The input object converts a sequence of characters from a stream into values stored in system memory.  The output object converts values stored in system memory into a sequence of characters in a stream.  Both objects are aware of the data type associated with the region of memory accessed and make appropriate conversions based upon that type. 

memory i o


Note that the data stored in regions of memory have types, while the data in the streams themselves are not associated with any type.


Output Objects

An output object is an instance of the ostream type that represents an output device.  An ostream object copies data from system memory into an output stream; in copying, it converts the data in system memory into a sequence of characters. 

The iostream module defines three standard output objects:

  • cout - sends a buffered sequence of characters to the standard output device
  • cerr - sends an unbuffered sequence of characters to the standard error output device
  • clog - sends a buffered sequence of characters to the standard error output device

Insertion

The expression for inserting data into an output stream takes the form

 outputObject << identifier

where outputObject is the name of the ostream object.  << is the insertion operator.  identifier is the name of the variable or the object that stores the data.

For example,

 int i = 6;
 char c = ' ';
 double x = 9.75;
 char s[] = "Harry";
 cout << i;
 cout << c;
 cout << x;
 cout << c;
 cout << s;
 cout << endl;
 cerr << "Data has been written";




 6 9.75 Harry



 Data has been written

Each expression with an ostream object as its left operand converts the data in its right operand into a sequence of characters according to the right operand's type. 

endl inserts a newline character into the stream and flushes the stream's buffer. 

Cascaded Insertion

We may combine these expressions into a single statement that specifies multiple insertions:

 int i = 6;
 char c = ' ';
 double x = 9.75;
 char s[] = "Harry";

 cout << i << c << x << c << s << endl;

 cerr << "Data has been written";





 6 9.75 Harry
 Data has been written

We call such repeated use of the insertion operator cascading

Member Functions

The ostream type exposes the following member functions for formatting conversions:

  • width(int) - sets the field width to the integer received
  • fill(char) - sets the padding character to the character received
  • setf(...) - sets a formatting flag to the flag received
  • unsetf(...) - unsets a formatting flag for the flag received
  • precision(int) - sets the decimal precision to the integer received

width

The width() member function specifies the minimum width of the next output field:

 // Field Width
 // width.cpp

 #include <iostream>
 using namespace std;

 int main() {
     int attendance = 27;
     cout << "1234567890" << endl;
     cout.width(10);
     cout << attendance << endl;
     cout << attendance << endl;
 }








 1234567890

         27
 27

width(int) applies only to the next field.  Note how the field width for the first output of attendance is 10, while the field width for the second output of attendance is just the minimum number of characters needed to display the value (2). 

fill

The fill() member function defines the padding character.  This is the character inserted into the stream wherever text occupies less space than the field width.  The default fill character is ' ' (space).  To pad a field with '*''s, we add:

 // Padding
 // fill.cpp

 #include <iostream>
 using namespace std;

 int main() {
     int attendance = 27;
     cout << "1234567890" << endl;
     cout.fill('*');
     cout.width(10);
     cout << attendance << endl;
 }







 1234567890


 ********27


The padding character remains unchanged, until we reset it.

setf, unsetf

The setf() and unsetf() member functions control alignment and formatting.  The control flags include:

Control Flag  Result
ios::left  align left 
ios::right  align right 
ios::scientific  d.ddddddEdd 
ios::fixed  ddd.ddd 

Note the scope resolution (ios::) on these flags. 

The default format is general format, which outputs data in the simplest way possible.  To output a fixed number of decimal places, we use fixed format.  To turn on fixed format, we pass ios::fixed to setf():

 // Fixed Format
 // fixed.cpp

 #include <iostream>
 using namespace std;

 int main() {
     double pi = 3.141592653;
     cout << "1234567890" << endl;
     cout.width(10);
     cout.setf(ios::fixed);
     cout << pi << endl;
 }







 1234567890



   3.141593

Format settings persist until we change them.  To unset fixed format, we pass the ios::fixed flag to the unsetf() member function:

 // Unset Fixed Format
 // unsetf.cpp

 #include <iostream>
 using namespace std;

 int main() {
     double pi = 3.141592653;
     cout << "1234567890" << endl;
     cout.width(10);
     cout.setf(ios::fixed);
     cout << pi << endl;
     cout.unsetf(ios::fixed);
     cout << pi << endl;
 }







 1234567890



   3.141593

 3.14159

To turn on scientific format, we pass the ios::scientific flag to the setf() member function:

 // Scientific Format
 // scientific.cpp

 #include <iostream>
 using namespace std;

 int main() {
     double pi = 3.141592653;
     cout << "12345678901234" << endl;
     cout.width(14);
     cout.setf(ios::scientific);
     cout << pi << endl;
 }







 12345678901234



   3.141593e+00

To turn off scientific format, we pass the ios::scientific flag to the unsetf() member function.

To turn on left-justification, we pass the ios::left flag to the setf() member function:

 // Left Justified
 // left.cpp

 #include <iostream>
 using namespace std;

 int main() {
     double pi = 3.141592653;
     cout << "1234567890" << endl;
     cout.width(10);
     cout.fill('?');
     cout.setf(ios::left);
     cout << pi << endl;
 }







 1234567890




 3.14159???

To turn off left-justification, we pass the ios::left flag to the unsetf() member function:

 cout.unsetf(ios::left);

The default alignment is right-justified. 

precision

The precision() member function sets the precision of the subsequent floating-point fields.  The default precision is 6 units.  General, fixed, and scientific formats count precision differently.  General format counts the number of significant digits.  Scientific and fixed formats count the number of digits following the decimal point.  For a precision of 2 under general format, we write

 // Precision
 // precison.cpp

 #include <iostream>
 using namespace std;

 int main() {
     double pi = 3.141592653;
     cout << "1234567890" << endl;
     cout.width(10);
     cout.precision(2);
     cout << pi << endl;
 }








 1234567890


        3.1

The precision setting applies to the output of all subsequent floating-point values until we change it. 

Manipulators

Manipulators provide an elegant alternative for modifying the format setting.  We use manipulators as operands to insertion operators.  Manipulators that take no arguments do not include parentheses. 

Manipulators that don't take arguments are defined in <iostream>.  Those that take arguments are defined in <iomanip>.  We include <iomanip> whenever we use manipulators that take arguments.

The insertion manipulators include: 

Manipulator  Effect
fixed  output floating-point numbers in fixed-point format
scientific  output floating-point numbers in scientific format
left  left justify
right  right justify
flush  flush the output buffer
endl  output end of line and flush the buffer
setprecision(int)  set the precision of the output
setfill(int)  set the fill character for the field width
setbase(int)  set the base of the number system for int output
setw(int)  set the field width for the next output operand only

Manipulators (except for setw(i) which only modifies settings temporarily) modify the format settings used by the insertion operator until we change them. 

For example,

 cout << fixed << left << setw(5) <<
        setprecision(1) << 12.376 <<
        setprecision(5) << 12.376 <<
        endl;

 12.4 12.37600


Reference Example

The following program produces the output listed on the right

 #include <iostream>
 #include <iomanip>
 using namespace std;

 int main( ) {
     /* integers */
     cout << "\n* ints *\n"
     << "1234567890\n"
     << "----------\n"
     << 4321 << '\n'
     << setw(7) << 4321 << '\n'
     << setw(7) << setfill('0') << 4321 << setfill(' ')<<'\n'
     << setw(7) << left << 4321 << right << '\n';
     /* floats */
     cout << "\n* floats *\n"
     << "1234567890\n"
     << "----------\n"
     << 4321.9876546F << '\n';
     /* doubles */
     cout << "\n* doubles *\n"
     << "1234567890\n"
     << "----------\n"
     << fixed << 4.9876546 << '\n'
     << setw(7) << setprecision(3) << 4.9876546 << '\n'
     << setw(7) << setfill('0') << 4.9876546 << '\n'
     << setw(7) << left << 4.9876546 << right << '\n';
     /* characters */
     cout << "\n* chars *\n"
     << "1234567890\n"
     << "----------\n"
     << 'd' << '\n'
     << int('d') << '\n';
 }






 * ints *
 1234567890
 ----------
 4321
    4321
 0004321
 4321

 * floats *
 1234567890
 ----------
 4321.99

 * doubles *
 1234567890
 ----------
 4.987655
   4.988
 004.988
 4.98800

 * chars *
 1234567890
 ----------
 d
 100

Note that

  • a double or a float rounds to the requested precision
  • char data displays in either character or decimal format: to output its numeric code, we cast the value to an int (the value output for 'd' here is its ASCII value). 

Input Object

The input object is an instance of the istream type that represents an input device.  The object extracts data from the input stream and stores it in system memory: it converts the sequence of characters in the stream into equivalent values in system memory. 

Extraction

The expression for extracting characters from an input stream takes the form

 inputObject >> identifier

where inputObject is the name of the input object.  >> is the extraction operator.  identifier is the name of the destination variable. 

The iostream library defines one standard input object for buffered input named cin

For example,

 int i;
 char c;
 double x;
 char s[8];
 cout << "Enter an integer,\n"
         "a character,\n"
         "a floating-point number and\n"
         "a string : " << flush;
 cin >> i;
 cin >> c;
 cin >> x;
 cin >> s;  // possible overflow
 cout << "Entered " << i << ' '
      << c << ' ' << x << ' ' << s << endl;




 Enter an integer,
 a character,
 a floating-point and
 a string : 6  -  9.75  Harry




 Entered 6 - 9.75 Harry

Each expression with an istream object as its left operand converts the next sequence of characters into the data type of its right operand. 

The cin object skips leading whitespace with numeric, string and character types (in the same way that scanf("%d"...), scanf("%lf"...), scanf("%s"...) and scanf(" %c"...) do in the C language). 

 // Leading Whitespace
 // leading.cpp

 #include <iostream>
 using namespace std;

 int main() {
     char str[11];

     cout << "Enter a string : " << endl;
     cin >> str;
     cout << "|" << str << "|" << endl;
 }



          Note: _ denotes space





 Enter a string :
 __abc
 |abc|

Whitespace

cin treats whitespace in the input stream as a delimiter for both numeric and string data types.  For C-style null-terminated string types, cin adds the null byte after the last non-whitespace character stored in memory: 

 // Trailing Whitespace
 // trailing.cpp

 #include <iostream>
 using namespace std;

 int main() {
     char str[11];

     cout << "Enter a string : " << endl;
     cin >> str;
     cout << "|" << str << "|" << endl;
 }



          Note: _ denotes space





 Enter a string :
 __abc__
 |abc|

Cascaded Extraction

The extraction operator (>>), like the insertion operator, accepts cascading: 

 int i;
 char c;
 double x;
 char s[8];
 cout << "Enter an integer,\n"
         "a character,\n"
         "a floating-point number and\n"
         "a string : " << flush;
 cin >> i >> c >> x >> s;
 cout << "Entered " << i << ' '
      << c << ' ' << x << ' ' << s << endl;




 Enter an integer,
 a character,
 a floating-point and
 a string : 6  -  9.75  Harry

 Entered 6 - 9.75 Harry

Overflow

In the above two examples overflow is possible when filling s.  The extraction operator >> does not restrict the number of characters accepted.  If there are more than 7 characters in the input stream some of the data stored may corrupt other memory as shown below: 

 // Overflow
 // overflow.cpp

 #include <iostream>
 using namespace std;

 int main() {
     int i;
     char c;
     double x;
     char s[8];
     cout << "Enter an integer,\n"
         "a character,\n"
         "a floating-point number and\n"
         "a string : \n";
     cin >> i >> c >> x >> s;
     cout << "Entered " << endl;
     cout << i << ' '
          << c << ' ' << x << ' ' << s << endl;
 }











 Enter an integer,
 a character,
 a floating-point and
 a string :
 6 - 9.75 Constantinople

 Entered
 6 - 2.04952 Constantinople

This result varies from platform to platform.

Member Functions

The istream type exposes the following member functions for controlling extraction:

  • ignore(...) - ignores/discards character(s) from the input buffer
  • get(...) - extracts a character or a string from the input buffer
  • getline(...) - extracts a line of characters from the input buffer

ignore

The ignore() member function extracts characters from the input buffer and discards the characters.  ignore() doesn't skip leading whitespace.  There are two versions of ignore()

 cin.ignore();
 cin.ignore(2000, '\n');  

The no-argument version discards a single character.  The two-argument version removes and discards up to the specified number of characters or up to the specified delimiting character, whichever occurs first and discards the delimiting character.  The default delimiter is end-of-file (not end-of-line). 

get

The get() member function extracts either a single character or a string from the input buffer.  There are three versions: 

 // Input Extraction Using get()
 // get.cpp

 #include <iostream>
 using namespace std;

 int main() {
     char c, d, t[8], u[8], v;

     c = cin.get();          // extracts a single character
     cin.get(d);             // extracts a single character
     cin.get(t, 8);          // newline delimiter - accepts up to 7 chars 
                             //    and adds a null byte
     cin.ignore(2000, '\n'); // extracts the 'j' and the newline
     cin.get(u, 8, '\t');    // tab delimiter - accepts up to 7 chars and
                             //    adds a null byte
     cin.ignore();           // extracts the tab
     cin.get(v);             // extracts a single character

     cout << "c = " << c << endl;
     cout << "d = " << d << endl;
     cout << "t = " << t << endl;
     cout << "u = " << u << endl;
     cout << "v = " << v << endl;
 }

The above program produces the following results:

 Input stream : abcdefghij
                klmn\topqr
 Output:
 -------
 c = a
 d = b
 t = cdefghi
 u = klmn
 v = o

get() does not skip leading whitespace.  get(,) leaves the delimiting character in the input buffer.  If we use get(,) we should remove the delimiting character, if there is one.  Both string versions - get(char*, int) and get(char*, int, char) - append a null byte to the sequence of characters stored in memory. 

getline

getline() behaves like get(), but extracts the delimiter from the input buffer:

 // Input Extraction Using getline()
 // getline.cpp

 #include <iostream>
 using namespace std;

 int main() {
     char t[8], u[8], v;

     cin.getline(t, 8);       // newline delimiter - accepts up to 7 chars 
                              //    and adds a null byte
     cin.getline(u, 8, '\t'); // tab delimiter - accepts up to 7 chars and
                              //    adds a null byte
     cin.get(v);              // extracts a single character

     cout << "t = " << t << endl;
     cout << "u = " << u << endl;
     cout << "v = " << v << endl;
 }

The above program produces the following results:


 Input stream : cdefghi
                jklmn\topqr
 Output:
 -------
 t = cdefghi
 u = jklmn
 v = o

getline(), like get(), does not skip leading whitespace and appends a null byte to the sequence of characters stored in system memory. 

Format Control

Manipulators

Manipulators for input objects that don't take arguments are defined in the <iostream> header file.  The one that takes an argument is defined in <iomanip>.  Manipulators that take no arguments do not include parentheses:

Manipulator  Effect
skipws  skip whitespace
noskipws  turn off skip whitespace
setw(int)  set the field width for next input (strings only)

The argument to setw() should be one more than the maximum number of input characters to be read.  Note that the setw() manipulator is an alternative to get(char*, int), but setw() skips leading whitespace unless we turn off skipping.

Once a manipulator has modified the format settings of an input object, those settings remain modified. 

We may combine manipulators with input variables directly.  For example,

 // Input Manipulators
 // manipulator.cpp

 #include <iostream>
 #include <iomanip>
 using namespace std;

 int main( ) {
     char a[5], b[2], c[7], d[7];
     cout << "Enter : ";
     cin >> setw(5) >> a >>
            setw(2) >> b >> noskipws >>
            c >> skipws >> d;
     cout << "Stored '" << a <<
             "' & '" <<  b <<
             "' & '" << c <<
             "' & '" << d << "'" << endl; 
 }










 Enter :     abcde          fgh



 Stored 'abcd' & 'e' & '' & 'fgh'




State

The ostream and istream types expose member functions for reporting and changing the state of their objects.  These member functions include:

  • good() - the next operation might succeed
  • eof() - end of data has been encountered
  • fail() - the next operation will fail
  • clear() - reset the state to good
  • bad() - the data may be corrupted

We should check the state of the input object every time that we extract a sequence of characters from the input buffer.  If the object encounters an invalid character, the object fails and leaves that invalid character in the input buffer.  The fail() member function reports such an encounter. 

Before the object can continue extracting data, we must clear it of its failed state.  The clear() member function resets the object's state to good:

 if(cin.fail()) {            // checks if cin is in a failed state
     cin.clear();            // clears state to allow further extraction
     cin.ignore(2000, '\n'); // clears the input buffer
 }

Robust Validation

The state member functions help us validate input robustly.  We check the input object's state with each extraction of a sequence of characters to ensure that some value was converted and that the converted value is within valid bounds.  We reject out-of-bound and invalid values, resetting any failed state, and requesting fresh input as necessary. 

getPosInt

To extract from standard input a positive int that is not greater than max, we write

 // getPosInt extracts a positive integer <= max
 // from standard input and returns its value
 //
 int getPosInt(int max) {
     int value;
     int keepreading;

     keepreading = 1;
     do {
         cout << "Enter a positive integer (<= " << max << ") : "; 
         cin  >> value;

         if (cin.fail()) {   // check for invalid character
             cerr << "Invalid character.  Try Again." << endl;
             cin.clear();
             cin.ignore(2000, '\n');
         } else if (value <= 0 || value > max) {
             cerr << value << " is outside the range [1," <<
                  max << ']' << endl;
             cerr << "Invalid input.  Try Again." << endl;
             cin.ignore(2000, '\n');
         } else if (char(cin.get()) != '\n') {
             cerr << "Trailing characters.  Try Again." << endl;
             cin.ignore(2000, '\n');
         } else
             keepreading = 0;
     } while(keepreading == 1);

     return value;
 }

Summary

  • the insertion and extraction operators accommodate cascading
  • field width settings only hold for the next field
  • all other settings persist until changed
  • precision has different meanings under general, scientific, and fixed format
  • manipulators provide an elegant alternative to member function-based format settings
  • manipulators that take arguments require #include <iomanip>
  • get() and getline() can read strings with whitespace
  • a failed state must be cleared before processing can continue

Exercises

  • Experiment with get(), getline() and state on little programs of your own
  • Complete the Handout on Input and Output



Previous Reading  Previous: Member Functions and Privacy Next: Dynamic Memory   Next Reading


  Designed by Chris Szalwinski   Copying From This Site   
Logo
Creative Commons License