istringstream
An istringstream
is often used when we have some work to do on an entire line, and other work to do with individual words within a line.
As one example, assume we have a file that lists people and their associated phone numbers. Some people have only one number, but others have several—a home phone, work phone, cell number, and so on. Our input file might look like the following:
morgan 2015552368 8625550123
drew 9735550130
lee 6095550132 2015550175 8005550000
Each record in this file starts with a name, which is followed by one or more phone numbers. We’ll start by defining a simple class to represent our input data:
// members are public by default; see § 7.2 (p. 268)
struct PersonInfo {
string name;
vector<string> phones;
};
Objects of type PersonInfo
will have one member that represents the person’s name and a vector
holding a varying number of associated phone numbers.
Our program will read the data file and build up a vector
of PersonInfo
. Each element in the vector
will correspond to one record in the file. We’ll process the input in a loop that reads a record and then extracts the name and phone numbers for each person:
string line, word; // will hold a line and word from input, respectively
vector<PersonInfo> people; // will hold all the records from the input
// read the input a line at a time until cin hits end-of-file (or another error)
while (getline(cin, line)) {
PersonInfo info; // create an object to hold this record's data
istringstream record(line); // bind record to the line we just read
record >> info.name; // read the name
while (record >> word) // read the phone numbers
info.phones.push_back(word); // and store them
people.push_back(info); // append this record to people
}
Here we use getline
to read an entire record from the standard input. If the call to getline
succeeds, then line
holds a record from the input file. Inside the while
we define a local PersonInfo
object to hold data from the current record.
Next we bind an istringstream
to the line that we just read. We can now use the input operator on that istringstream
to read each element in the current record. We first read the name followed by a while
loop that will read the phone numbers for that person.
The inner while
ends when we’ve read all the data in line
. This loop works analogously to others we’ve written to read cin
. The difference is that this loop reads data from a string
rather than from the standard input. When the string
has been completely read, “end-of-file” is signaled and the next input operation on record
will fail.
We end the outer while
loop by appending the PersonInfo
we just processed to the vector
. The outer while
continues until we hit end-of-file on cin
.
Exercise 8.9: Use the function you wrote for the first exercise in § 8.1.2 (p. 314) to print the contents of an istringstream
object.
Exercise 8.10: Write a program to store each line from a file in a vector<string>
. Now use an istringstream
to read each element from the vector
a word at a time.
Exercise 8.11: The program in this section defined its istringstream
object inside the outer while
loop. What changes would you need to make if record
were defined outside that loop? Rewrite the program, moving the definition of record
outside the while
, and see whether you thought of all the changes that are needed.
Exercise 8.12: Why didn’t we use in-class initializers in PersonInfo
?