Search Contact information
University of Cambridge Home Department of Engineering
University of Cambridge >  Engineering Department >  computing help >  Languages >  C++

C++ Input/Output

Concepts

Users want a high degree of control over formatting, but also want high performance and ease of use. Before we see how C++ attempts to reconsile these needs, let's consider I/O in more detail in an attempt to justify C++'s solution.
  • streams - I/O uses the concept of streams - a flow of characters. These characters may be 1 byte or 2 byte ('wide'), the latter necessary for languages with many characters. streams may flow into or out of files. They can also flow into and out of strings. C++ tries to offer the same set of commands whatever the nature of the source and destination.
  • buffers - It would be inefficient to update a file on disc each time a character was added. The character is written into a buffer, and when enough characters are in the buffer to make updating worthwhile, the buffer is flushed and the file on disc updated. C++ offers ways to control the behaviour of buffers, but often the default is ok.
  • state - Each stream has state information indicating whether an error has occured, etc.
  • locale - The natural language required by the user has an influence on output - should a bool value be printed out as true or vero? Such preferences are controlled by the locale, which is usually set up appropriately by default. The locales section has a example of using this feature.
True to form, C++ represents each distinct concept with a class, and tries to 'factor out' components that are needed in more than one class. Usually users don't have to worry too much about this (skip to the Input section now if you want), but some awareness of this structure will help you understand the online documentation and help you decide which include files to use.
Contents

Classes

The class hierarchy (inheritance tree) is as follows
                   ios_base
                     ios
                      /\
                     /  \
                    /    \
               istream   ostream
              /    |  \ /  |    \
             /     |   |   |     \
            /     /    |    \     \
           /     /     |     \     \
          /     /      |      \     \
   istring ifstream iostream ofstream ostring
   stream             /\               stream
                     /  \
               fstream  stringstream
so if the user used a ifstream to get input from a file, the istream (input) features, state information (in ios_base) and format/buffer control (via ios) would be available.

Predefined streams

The following streams are predefined:

Input

Initialisation

To input data you first need to create a stream and associate it with a file or string so that the stream knows where to get the characters from. Unless you're using cin (the standard input stream - usually the keyboard) you'll need to use the open command, or a constructor. You can provide extra arguments, but usually you just need to provide a filename. As a simple example consider the following, which reads then displays 3 numbers from a file.
#include <iostream> #include <fstream> using namespace std; int main() { int i; ifstream fin; fin.open("test"); // test contains 3 ints for (int j=0;j<3;j++) { fin >> i; cout << i << endl; } fin.close(); }

This program lacks error-checking - if the file doesn't exist, or if it contains less than 3 integers then the program won't work well. Even if the file's ok, it's rather dangerous to ever read from it before checking first. For instance, in the program above if you put the fin.close(); line inside the loop (at the end), the stream will be closed after the first number is read, but the program will still happily print erroneous values out for the last two numbers and won't complain.

There are various way to check on the success of a call. Here are a few - see the Exceptions section for an alternative

To read a line of text (including spaces) into a string, use getline()
#include <string> #include <fstream> #include <iostream> using namespace std; int main() { string s; ifstream fin; fin.open("/etc/passwd"); while(getline(fin,s)) cout << s << endl; }

The file will be opened at its start. If you don't want to access the contents sequentially, you can use seekg() to set the file-position indicator, and tellg() to tell you the current position. The following program prints the first and last characters of the file given to it as an argument. Note the use of ios::beg (beg is defined in ios) to show that you're offsetting from the beginning of the file.

#include <string> #include <fstream> #include <iostream> using namespace std; int main(int argc, char *argv[]) { string fileName; bool flag=false; if (argc==2) fileName=argv[1]; else { cerr << "Provide a single filename next time" << endl; return 1; } ifstream fin; fin.open(fileName.c_str()); while(fin.good() == false) { cout << "Unable to open file. Enter a new name: "; cin >> fileName; fin.open(fileName.c_str()); } istream ist(fin.rdbuf()); ist.seekg(0,ios::beg); streampos pos=ist.tellg(); cout << "Initial Position=" << pos << endl; char c; ist.get(c); cout << "Char=" << c<< endl; ist.seekg(-1,ios::end); pos=ist.tellg(); cout << "Final Position=" << pos << endl; ist.get(c); cout << "Char=" << c <<endl; return 0; }
As usual, care needs to be taken when using these routines in case they fail. If get can't read a character it sets a failure flag, which causes the member function fail() to subsequently return true. When the file is in this state calling seekg has no effect and tellg returns -1 indicating failure.

Routines

As well as being able to use ">>" as usual on an open file there are also various forms of the get command (to read raw data) as well as getline to read a line of text with spaces. You need to read the documentation carefully. In particular, input routines differ in how they deal with a final new-line character. '>>' will stop reading input when it reaches a final new-line character, but it won't remove the new-line character from the stream. This might confuse the next input routine, which on reading the initial new-line character might assume that it denotes an empty line (though '>>' skips any leading newlines). If you try the following, you won't have a chance to enter a character array.
#include <iostream> using namespace std; int main() { char s[100]; char c; cout << "Enter a character: "; cin >> c; cout << "Enter a string: "; cin.getline(s,25); }
One way to cure this is by adding
cin.ignore(INT_MAX, '\n' );
before trying to read the string. This removes the newline character.

Note also that text files on different types of operating system may have different ways of indicating the end of a text line. See our End-of-line characters in text files page.

Other member functions include And remember to call the close() member function when you've finished with a file.

Manipulators

When, during output, you use "<< endl" you are using a manipulator. There are a few input manipulators too

Output

Initialisation

To output data you first need to create a stream and associate it with a file or string so that the stream knows where to deliver the characters. You can use a constructor or the open member function to open a file for writing.

Routines

Once the stream is in operation you can use "<<" as usual. So
... ofstream fout; fout.open("MyFilename"); fout << "test";
should work. Other member functions include

Manipulators and Formatting

This requires C++ knowing how to convert values of various types into characters. C++ can deal with the standard types automatically, but even with these the user may wish to format the output. Formatting is controlled by flags in ios_base. These can be set using the member functions below or by using manipulators The setf function can be called to set flags, and the following manipulators are available Here's a short example
#include <iostream> #include <iomanip> // to use the setprecision manipulator using namespace std; int main() { cout << 1331 <<endl; cout << "In hex " << hex<< 1331 <<endl; cout << 1331.123456 <<endl; cout.setf(ios::scientific,ios::floatfield); cout <<1331.123456 <<endl; cout << setprecision(3) << 1331.123456 <<endl; cout << dec << 1331 <<endl; cout.fill('X'); cout.width(8); cout << 1331 <<endl; cout.setf(ios::left,ios::adjustfield); cout.width(8); cout << 1331 <<endl; return 0; }

Booleans

You can print out bools as numerical values or as words. The following should print out "1" then "true" (translated into your chosen language).
cout << true << endl; cout << boolalpha; cout << true << endl;

Output of User-defined types

You can output user-defined types directly using cout, but you need to write an extra routine first. Here's an example
#include <iostream> #include <string> using namespace std; class test { public: int age; string name; }; ostream& operator<<(ostream &os, const test &t) { return os << t.name << " is " << t.age << " years old"; } int main() { test t; t.age = 11; t.name ="Jan"; cout << t << endl; }

Exceptions

If you don't know what exceptions are, just skip to the next section - you can survive without them for the moment. By default, errors involving streams don't generate exceptions. Exceptions can be enabled individually. Earlier on, good() was used to check on success. Here's another way to check whether file-opening succeeds.
#include <iostream> #include <fstream> #include <exception> using namespace std; int main() { try { fstream fin; fin.exceptions( fstream::failbit ); fin.open("test"); } catch (std::exception &e) { cout << "Exception caught: " << e.what() << endl; } }

Strings

std::string and character arrays

Wherever possible, C++ strings (rather than character arrays) have been used here. In most of the situations above, character arrays can be used instead of strings. Points worth noting are

Writing to and reading from Strings

Once you can do I/O to and from files, I/O to and from strings is very similar, except that you don't need an "open()" call. The following example writes (i.e. converts) an integer into a string.
#include <sstream> #include <string> #include <iostream> using namespace std; int main() { stringstream s; string t; int x = 10; s << x; s >> t; cout << "t=" << t << endl; return 0; }

locales

You probably won't need to use locales explicitly. The list of available locales is system-dependent - on Unix, typing locale -a should list them. Here's an example of their use, showing how output is affected.
#include <iostream> #include <locale> using namespace std; float f=1234.567; int main() { cout.imbue(locale("uk_UA")); cout << "Ukranian " << f << endl; cout.imbue(locale("en_GB")); cout << "UK English " << f << endl; cout.imbue(locale("it_IT")); cout << "Italian " << f << endl; return 0; }

Wide characters

In situations where each character requires 2 bytes, you can use wide characters.
#include <iostream> using namespace std; int main() { wstring str(L"hello"); wcout << str << endl; }

See also

cplusplus.com's iostream library reference describes the functions available, with sections on iostream, fstream, stringstream, etc.

© Cambridge University Engineering Dept
Information provided by Tim Love (tpl)
Last updated: April 2008