Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Chapter 14

#include <iostream> #include <iomanip>

using namespace std;

int main(int argc, char** argv)

{

bool myBool = true;

cout << “This should be true: “ << boolalpha << myBool << endl; cout << “This should be 1: “ << noboolalpha << myBool << endl; double dbl = 1.452;

cout << “This should be @@1.452: “ << setw(7) << setfill(‘@’) << dbl << endl;

}

If you don’t care for the concept of manipulators, you can usually get by without them. Streams provide much of the same functionality through equivalent methods like setPrecision(). See Appendix B for details.

Input with Streams

Input streams provide a simple way to read in structured or unstructured data. In this section, the techniques for input are discussed within the context of cin, the console input stream.

Input Basics

There are two easy ways to read data using an input stream. The first is an analog of the << operator that outputs data to an output stream. The corresponding operator for reading data is >>. When you use >> to read data from an input stream, the variable you provide is the storage for the received value. For example, the following program reads a line of input from the user and puts it into a string. Then the string is output back to the console.

#include <iostream> #include <string>

using namespace std;

int main(int argc, char** argv)

{

string userInput; cin >> userInput;

cout << “User input was “ << userInput << endl;

}

By default, the input stream will tokenize values according to white space. For example, if a user runs the previous program and enters hello there as input, only the characters up to the first white space character (the space character in this instance) will be captured into the userInput variable. The output would be:

User input was hello

The >> operator works with different variable types, just like the << operator. For example, to read an integer, the code differs only in the type of the variable:

384

Demystifying C++ I/O

#include <iostream> using namespace std;

int main(int argc, char** argv)

{

int userInput; cin >> userInput;

cout << “User input was “ << userInput << endl;

}

You can use input streams to read in multiple values, mixing and matching types as necessary. For example, the following function, an excerpt from a restaurant reservation system, asks the user for a last name and number of people in their party.

void getReservationData()

{

string guestName; int partySize;

cout << “Name and number of guests: “;

cin >> guestName >> partySize;

cout << “Thank you, “ << guestName << “.” << endl; if (partySize > 10) {

cout << “An extra gratuity will apply.” << endl;

}

}

Note that even though the use of cout does not explicitly flush the buffer using endl or flush(), the text will still be written to the console because the use of cin immediately flushes the cout buffer. cin and cout are linked together in this way.

If you get confused between << and >>, just think of the angles as pointing towards their destination. In an input stream, << points toward the stream itself because data are being sent to the stream. In an output stream, >> points toward the variables because data are being stored.

Input Methods

Just like output streams, input streams have several methods that allow a lower level of access than the functionality provided by the more common >> operator.

get()

The get() method allows raw input of data from a stream. The simplest version of get() simply returns the next character in the stream, though other versions exist that read multiple characters at once. () is most commonly used to avoid the automatic tokenization that occurs with the >> operator. For example, the following function reads a name, which can be made up of several words, from an input stream until the end of the stream is reached.

string readName(istream& inStream)

{

string name;

while (inStream.good()) {

int next = inStream.get(); if (next == EOF) break;

385

Chapter 14

name += next;// Implicitly convert to a char and append.

}

return name;

}

There are several interesting observations to make about the previous function. First, its parameter is a reference to an istream, not a const reference. The methods that read in data from a stream will change the actual stream (most notably, its position), so they are not const methods. Thus, you can’t call them on a const reference. Second, the return value of get() is stored in an int, not in a char. Because get() can return special noncharacter values such as EOF (end of file), ints are used. When next is appended to the string, it is implicitly converted to a char.

The previous function is a bit strange because there are two ways to get out of the loop. Either the stream can get into a “not good” state, or the end of the stream is reached. A more common pattern for reading from a stream uses a different version of get() that takes a reference to a character and returns a reference to the stream. This pattern takes advantage of the fact that evaluating an input stream within a conditional context will result in true only if the stream is available for additional reading. Encountering an error or reaching the end-of-file both cause the stream to evaluate to false. The underlying details of the conversion operations required to implement this feature are explained in Chapter 16. The following version of the same function is a bit more concise.

string readName(istream& inStream)

{

string name;

char next; while (inStream.get(next)) { name += next;

}

return name;

}

unget()

For most purposes, the correct way to think of an input stream is as a one-way chute. Data falls down the chute and into variables. The unget() method breaks this model in a way by allowing you to push data back up the chute.

A single call to unget() causes the stream to back up by one position, essentially putting the last character read back on the stream.

char ch1, ch2, ch3;

in >> ch1 >> ch2 >> ch3; in.unget();

char testChar; in >> testChar;

// testChar == ch3 at this point

386

Demystifying C++ I/O

putback()

putback(), like unget(), lets you move backward by one character in an input stream. The difference is that the putback() method takes the character being placed back on the stream as a parameter:

char ch1;

in >> ch1;

in.putback(ch1);

// ch1 will be the next character read off the stream.

peek ()

The peek() method allows you to preview the next value that would be returned if you were to call get(). To take the chute metaphor perhaps a bit too far, you could think of it as looking up the chute without a value actually falling down it.

peek() is ideal for any situation where you need to look ahead before reading a value. For example, your program may do something different, depending on whether the next value starts with a number, as in the following code snippet.

int next = cin.peek(); if (isdigit(next)) { processNumber();

} else { processText();

}

getline()

Obtaining a single line of data from an input stream is so common that a method exists to do it for you. The getline() method fills a character buffer with a line of data up to the specified size, as in the following code.

char buffer[kBufferSize + 1];

cin.getline(buffer, kBufferSize);

Note that getline() removes the newline character from the stream. However, the resulting string will not include the newline character. There is a form of get() that performs the same operation as getline(), except that it leaves the newline character in the input stream.

There is also a function called getline() that can be used with C++ strings. It is defined in the std namespace and takes a stream reference, a string reference, and an optional delimeter as parameters:

string myString;

std::getline(cin, myString);

Handling Input Errors

Input streams have a number of methods to detect unusual circumstances. Most of the error conditions related to input streams occur when there is no data available to read. For example, the end of stream

387

Chapter 14

(referred to as end of file, even for nonfile streams) may have been reached. The most common way of querying the state of an input stream is to access it within a conditional, as above. You can also call the good() method, just like an output stream. There is also an eof() method that returns true if the stream has reached its end.

You should also get in the habit of checking the stream state after reading data so that you can recover from bad input.

The following program shows the common pattern for reading data from a stream and handling errors. The program reads numbers from standard input and displays their sum once end of file is reached. Note that in most command-line environments, end of file is indicated on the console with control-D.

#include <iostream> #include <fstream> #include <string>

using namespace std;

int main()

{

int sum = 0;

if (!cin.good()) {

cout << “Standard input is in a bad state!” << endl; exit(1);

}

int number; while (true) {

cin >> number;

if (cin.good()) { sum += number;

} else if (cin.eof()) {

break; // Reached end of file

}else {

//Error!

cin.clear(); // Clear the error state. string badToken;

cin >> badToken; // Consume the bad input.

cerr << “WARNING: Bad input encountered: “ << badToken << endl;

}

}

cout << “The sum is “ << sum << endl;

return 0;

}

Input Manipulators

The built-in input manipulators, described in the list that follows, can be sent to an input stream to customize the way that data is read.

388