2.6 Basic Input/Output




Stream Operators

Operator overloading is the basis for input and output (I/O) in C++. Overloading is useful for I/O because the same operation (input or output) is being performed, but it is being performed on different types. This is exactly the situation that operator overloading is meant to address. In C++, each type has an overloaded operator that is programmed to handle I/O for that specific type. Thus, the same operator is used for all types. At compile time the type of the data being input or output determines which of the overloaded operations will be used.

The "operator" that is overloaded to handle I/O is not an operator with a name like "input" or "output" but is a symbolic operator. The symbolic operator "<<" is used to represent output and the symbolic operator ">>" is used to represent input. It will be seen later that most of the C++ operators (e.g., +, -, *, /, <, >, =, ==) can be overloaded.

In C++, I/O is based on a stream model. In a stream model the input data is viewed as a continuous stream of data that flows from a source into the the sequence of variables presented to the input stream. The type of the variables determines how the input stream is interpreted to provide values for the variables. On output, the values of variables flow into a (logically) continuous steam to the destination. The source (for input) and the destination (for output) may be the user or a file.

Interactive stream I/O is provided by two classes:


  class istream {...};		// stream input
  class ostream {...};		// stream output
These classes use elements of the C++ language that have not been covered yet. The study of the C++ I/O classes is taken up later. For now, a minimal understanding of how the use these classes is sufficient.

The standard C++ I/O library includes two predefined variables:

	istream cin;		// interactive input
 	ostream cout;		// interactive output
These variables are declared in the file "stream.h" that can be included in a program using the include directive:
	#include <stream.h>
Any single built-in type can be output by the "<<" operator as shown in the following example:

	cout << 10;		// output an integer
	cout << 1.234;		// output a float
	cout << 'x';		// output a character
	cout << "Hello World."	// output a string
	cout << '\n';		// output a "newline" character
Because output is viewed as a stream, the five statements above can be reduced to the following single line:
	cout <<  10  << 1.234 << 'x' << "Hello World." << '\n';
The values are entered into the output stream in a left-to-right order. For readability, blanks are often inserted into the output stream as in this example:

	cout << 10 << "  " << 20 << '\n';
that inserts two blanks between the 10 and the 20. The newline character ('\n') can be used to end a line of output. As an alternative to the newline character the standard I/O library defines a symbol "endl" (for "end line") that will also end the current line of output. The above example can be re-written as:

	cout << 10 << "  " << 20 << endl;
using the endl symbol.

Notice that there is no relationship between the number of lines of code that produce output and the number of lines of text that appear in the output. For example, the following three code fragments each output the same two lines of text:


   (1)	cout << 10;
	cout << endl;
	cout << 20 << endl;

	
   (2)  cout << 10 << endl << 20 << endl;

   (3)  cout 10 << endl << 20;
	cout << endl;

The values of variables can be output using the stream operators as shown in the following example:


	int x, y;		// two integers
	char c;			// a character
	char *s = "Hello";	// a string
	float z = 1.415;	// a floating point value

 	x = 100; y = 200;

	cout << x << "  " << y << endl << s << z << endl;
Produces the output string:
	100  200  
	Hello  1.415
The output will appear as two line because the endl symbol appeared twice in the output stream.

Interactive input uses the predefined variable "cin" and the ">>" operator in a similar manner. For example, the statements:

	int x;

	cin >> x; 
Cause an integer value to be read from the "standard input" and assigned as the value of the variable x. When reading, "whitespace (blanks, tabs) are ignored.

A dialog with the user usually consists of a prompt-response sequence. The program prompts the user to enter data and then reads that data. For example a program that wants to read two integer values that represent the hour and minute might look like:


	int hour, minute;

	cout << "Enter hour (integer) and minute (integer): " << endl;
	
	cin >> hour >> minute;

Input from and output to disk files use a similar strategy. Two additional classes are defined in the standard C++ library:

	class ifstream {
 	  private:
	    ...
	  public:
	    ifstream(char* filename);	// name of file to use for input
	    ...
	};


	class ofstream {
	  private:
	    ...
	  public:
	    ofstream(char* filename);	//name of file to use for output
	    ...
        };
The following example shows how data in files is manipulated using the stream I/O operators:

	ifstream is("file.dat");
	ofstream os("out.dat");
  
	int x, y;

	is >> x >> y;			// reads two integers from file.dat

        os << "The sum is:  ";		// output heading to out.dat
   	os << (x + y) << '\n';		// output sum and end line

	

Stream Output to a Window

A variation of the Frame class, named TextFrame, is introduced to allow stream output to a window. This class presents a very simplified version of the stream I/O model. It is restricted so that only output is possible and only for the most basic built-in types (int, long, float, double, char, and char*).

A partial definition of the TextFrame class is shown below.The constructors for a TextFrame are similar to those for a Frame object as are the MoveTo and Resize methods. Keep in mind that the TextFrame class is not part of the standard C++ library; like the Frame class, it is only part of the materials used here for learning about C++.


class TextFrame {
 private:
		// encapsulated, hidden data
 public:

   TextFrame(char *name, int x, int y, int w, int h);
   TextFrame(char* name, int x, int y);
   TextFrame(char* name);
   TextFrame();

  ~TextFrame(); 

   void     MoveTo( Location newLocation);      // change position
   void     Resize( Shape newShape);            // change shape
   void     Resize( float factor);              //
   int      IsNamed(char* n);                   // is this your name?
   
   ...

};

The TextFrame class can be used as shown n the code below. This code creates two Frame objects and a TextFrame object. The code output to the TextFrame the name of the Frame object in which each mouse event occurs.

  Frame window1("Window1", 100,100, 200, 200);
  Frame window2("Window2", 400,400, 200, 200);
  TextFrame out("Display", 400, 20, 200, 200);

  OnStart() {
    window1.DrawText("Click in this window", 20,20);
    window2.DrawText("Click in this window", 20,20);
    out << "Name of window clicked in will appear below" << '\n';
  }

  OnPaint() {
    window1.DrawText("Click in this window", 20,20);
    window2.DrawText("Click in this window", 20,20);
    out << "Name of window clicked in will appear below" << '\n';
  }
 
  OnMouseEvent(char* frameName, int x, int y, int buttonState) {
    out << name ;
    out << '\';
  }

  OnTimerEvent() {}

The TextFrame class is useful in displaying textual information to a user and is also convenient for displaying status information during development, testing and debugging.

String Streams

The stream I/O facilities may be used to transfer formatted data to a character array or read formatted data from a character array. Although the stream I/O operators are used, the stream operators in this case only cause the transfer of information between the character array and other variables in memory. The string stream operations do not involve an input or output device or file.

Stream processing using a character array is useful when formatted data (e.g., several integer values) is being passed to an interface that accepts only a single string (a char*) parameter. This occurs, for example, in the Frame class's DrawText method. To display one or more integer values in a Frame using DrawText, the integer data must first be converted to a single string. Using the stream output operators on a "string stream" the integer data is "written" into the string stream. The string stream places the data in a character array. The stream output operations maintain and end-of-string character ('\0') after the last character added to the character array. Thus, the character array can then be handled as a normal character array or string. It is also possible to use the stream input operators to "read" formatted data from a character array.

String stream processing involves two classes that are define in the standard C++ library:


   class istrstream {...};
   class ostrstream {...};

The constructor for a string stream object requires a pointer to the beginning of the character array (or memory buffer) and an integer argument giving the length of the array (or buffer). Once constructed, the stream output operator (<<) can be applied to an ostrstream object and the stream input operator (>>) can be applied to an istrstream object. An example of using string stream processing is shown in the example below. This example writes to a character array a simple arithmetic expression to add two integer values. The simple expression is formatted using the output string stream object named "expression" and parsed using a input stream stream object named "parser".

   char text[100];

   ostrstream expression(text, 100);	// create string stream

   expression << 10 << " + " << 20 << endl;

   ...

   istrstream parser(text, 100);

   int value1, value2;
   char operator;

   parser >> value1		// value1   = 10
          >> operator		// operator = '+'
          >> value2;		// value2   = 20


Notice that the two string streams are constructed using the "text" character array. It is into this character array that "expression" places its formatted data and from which "parser" reads characters to produce the formatted data requested of it.

Other Methods on Streams

In addition to the stream I/O operators, stream objects also provide moethds that invoked using the "dot" operator. These methods provide ways to query the status of the stream and to perform operations that are not readily described by an operator notation.

Examples of operators that query the state of the stream are those that test whether the end of an input stream has been reached or whether the last operation performed ona stream succeeded or failed. These two methods are used in the following code fragment:


	if (cin.eof()) ...	// at end of standard input stream
	if (cin.fail()) ...	// last input operation failed

An operation may fail, for example, if the stream was unable to parse the contents input stream to produce a value of the required type. For example, if an integer is to be read next from the input stream and the next text in the input stream is "abcd" that cannot be read as an integer value.

Reading an entire string or an entire line of input is also done through methods that use the "dot" operator because this operation does ot fit the stream operator model (i.e., there is no data type that corresponds to a line of input in C++). The method for reading a string or line of input is named "get" and it has three parameters:

  1. a character array into which the data is read,
  2. the maximum number of characters to be placed in the character array from the input stream (not to exceed one less than the length of the character array), and
  3. a character which, if encountered in the stream, will terminate the reading.
Several details about the get method that help to understand its operation are these: An example of using the get method to read a line of input is shown in the following example:
   char line[100];

   ...

   cin.get(line, 100, '\n');

Notice that the "dot" operator has been used to apply the get method to the standard input stream object.

Next Stop


Exercises

  1. Write a program that creates a Frame object with all default values. The program then reads location and shape information and a character string from a file named "window.dat". The Frame object is then moved and resized using the location and shape information read from the file and the character string read from the file is written into the Frame object.

  2. Mouse Tracker: Write a program that displays in a Frame the current mouse coordinates. The coordinates should be written in the form (x,y) - including the parentheses and comma - at the current mouse location. A string stream must be used to format the string to be written in the Frame

  3. Simplest Timer: Write a program to output to a TextFrame the current value of an integer variable that is incremented and output on each timer event. The TextFrame shoud initially display a zero.

  4. Mouse Reporter: Write a program to output to a TextFrame a line each time the left mouse button is clecked in a Frame object. The line of output should be of the form (x,y) - including the parentheses and comma - where x and y are the coordinates of the mouse event.

  5. Mouse Reporter 2: Write a program that extends the Mouse Reporter program by having two Frame objects and the line of output in the TextFrame is of the form name:(x,y) where name is the name of the Frame in which the mouse click occurred and x and y are the coordinates of the mouse event.

  6. File Viewer: Write a program that reads a disk file named "viewer.dat" a line at a time and writes each line to a TextFrame.

  7. File Scanner: Revise the FileViewer program so that a single line of text from the file is read and written to the TextFrame on each timer event.

Last Updated: September 3, 1996 / kafura@cs.vt.edu