C++: I/O Streams
Plain C has library-function based I/O capabilities, centered around the printf/scanf family of functions (and others).
C++ introduced a new OO-designed I/O package called iostreams, and embodied in the header . Some people love it, and some people hate it, but it is here to stay.
To use iostreams, you have to do
#include <iostream>
You will probably also include other headers, like string and others.
Iostreams give you three standard channels, the
standard input, called std::cin
, the standard
output, called std::cout
, and the standard error
output, called std::cerr
.
Many people will do using namespace std;
after the include line above, and
then directly use the symbols cin
, cout
, and cerr
without the std::
qualification. This is bad practice, because the namespace std
has
many many symbols that get imported in the using
statement, and some
may conflict with your own names. I strongly recommend just using
std::cout
, etc., rather than importing. If you really want to use the
short versions, then do using std::cout;
and only import that
single symbol (and then do the same for cin
and cerr
if you need to.
Output
Output is done using the <<
operator, with
std::cout
or std::cerr
(or other file stream handle)
on the left, and then multiple right-hand arguments, each
separated by <<
.
The constant named std::endl
represents a newline
character. You can use it to end the current output line and start
a new line, but newer practice actually prefers just using
a \n
character in a string.
The two lines below do the same thing:
std::cout << "my int var is " << i << " and my double var is " << d << std::endl;
std::cout << "my int var is " << i << " and my double var is " << d << "\n";
Input
Input is done using the >>
operator with
std::cin
on the left (or other input file stream handle),
and multiple variables on the right. For example,
std::cin >> i >> d;
will input an integer value and then a double value, and assign them to the variables i and d, respectively.
Although it looks wierd, it is actually safer than the printf/scanf form, because the compiler can check and verify that the values being assigned to the variables are of the same type. A plain C compiler cannot do the same types of checks with printf/scanf.
String-based Input
Input streams is a generic feature and not just useful for std::cin
; indeed, most of our data comes from files and other sources. If you have some user input in the form of a single std:string
, and you need to extract data from it, you can create a “string stream” object that will let you use the >>
operator to extract data pieces.
The first line below declares and constructs an object named lineStream
from a string named line
. The lineStream
can then be used as an input stream, as shown below on the third line.
std::istringstream lineStream(line);
...
lineStream >> myIntVar; // example
Line-Oriented File Input
While input stream input can be great in many places, many data files, especially human-readable data formats like Comma-Separated-Values, are line oriented, meaning that each line is some individual record of data. The input stream >>
operator generally ignores newlines, and so if it gets off due to a bad input file, the rest of the data will be read incorrectly!
It is much safer to process line-oriented data files in a line-oriented fashion. The code below is an example of doing this, using the std::getline()
function rather than the >>
operator. Each line is then a std::string
object, and we use the string stream capability to parse the data out of it (oddly enough, the std::getline()
function is still a good way to do this, but you could use the >>
operator if your data format was suitable).
#include <iostream>
#include <fstream>
#include <sstream>
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cerr << "Error: a filename argument must be given\n";
return -1;
}
// create an input stream object for the filename given as an argument;
// this also opens the file for reading
std::ifstream inFile(argv[1]);
std::string line;
if (!inFile) {
std::cerr << "Error: file " << argv[1] << " cannot be opened\n";
return -1;
}
// read the file line by line; this is safer than trying to
// parse the lines as you read the data in
while (std::getline(inFile, line)) {
std::cout << "line is [" << line << "]\n";
if (line.length() < 2)
continue; // probably at end of file
std::istringstream lineStream(line);
std::string piece;
// loop below separates pieces of the line by commas
while (std::getline(lineStream, piece, ',')) {
// remove quotes if they are part of the piece string
if (piece.front() == '"' && piece.back() == '"')
piece = piece.substr(1,piece.size()-2);
std::cout << " piece [" << piece << "]\n";
}
}
inFile.close();
std::cout << "Done processing the file.\n";
return 0;
}