String streams
A string stream is a stream whose source or destination is a std::string. An istringstream reads from text already in memory, an ostringstream builds text there, and both use the same operators as std::cin and std::cout.
That makes them the tool for a line that has to be parsed twice, for text assembled from parts with formatting, and for a function that returns a formatted string without printing it. The stream machinery of lesson 066 applies unchanged.
A string stream owns a copy of its buffer, and str() returns another copy; for large text, building a std::string directly or formatting once is cheaper.
Parsing a line
An istringstream is constructed from a string and read with >>. The operator skips whitespace and converts each field to the type of the variable, so a line of numbers becomes ints without a manual conversion.
The program reads two ints and a word from one line and prints their sum and the word. The stream stops at the end of the text, and the state of the stream says whether every extraction succeeded.
This is the standard way to parse a line with a fixed shape: read the fields in order, then check the stream. A line that comes from a file is no different, which is what lesson 070 builds on.
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::istringstream input{"12 34 hello"};
int first = 0;
int second = 0;
std::string word;
input >> first >> second >> word;
std::cout << first + second << ' ' << word << '\n';
}
g++ -std=c++23 -Wall -Wextra parse.cpp -o parse
./parse
46 hello
Building a line
An ostringstream accepts the same << calls as std::cout and keeps the result in an internal string. str() returns a copy of it, and the stream can be reused after a call to str("").
The program builds a sentence from strings and numbers and prints it with its length. Nothing is written to a terminal until the final line, so the text can be returned from a function or stored.
The alternative is std::format from lesson 063, which is shorter for a single line. A string stream is the better tool when the text is built incrementally or with the manipulators of the next section.
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::ostringstream line;
line << "user " << 7 << " has " << 3 << " items";
std::string text = line.str();
std::cout << text << ' ' << text.size() << '\n';
}
g++ -std=c++23 -Wall -Wextra build.cpp -o build
./build
user 7 has 3 items 18
Splitting on a delimiter
std::getline with a third argument reads up to the delimiter instead of a newline. Called in a loop on an istringstream, it splits a line into fields, and it keeps empty fields between two delimiters.
The program splits "one,two,,three" into four fields and prints each in brackets, which shows the empty third field. The count is four, not three.
The loop ends when getline returns a stream that is false, which happens at the end of the text. A trailing delimiter adds a final empty field, and code that does not expect one should check.
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::istringstream input{"one,two,,three"};
std::string field;
int count = 0;
while (std::getline(input, field, ',')) {
std::cout << '[' << field << ']';
++count;
}
std::cout << ' ' << count << '\n';
}
g++ -std=c++23 -Wall -Wextra split.cpp -o split
./split
[one][two][][three] 4
Formatting into a string
The manipulators of <iomanip> work on a string stream. std::fixed and std::setprecision set the number format, and std::setw sets the width of the next output only.
The program writes a double into an ostringstream with two decimals and a width of eight, then prints the result in brackets. The spaces before the number are the padding that setw added.
std::setw applies to one value and then resets; the other manipulators stay set until they are changed. Lesson 066 covers the full set, including the base and the fill character.
#include <iomanip>
#include <iostream>
#include <sstream>
int main()
{
std::ostringstream out;
out << std::fixed << std::setprecision(2) << std::setw(8) << 3.14159;
std::cout << '[' << out.str() << ']' << '\n';
}
g++ -std=c++23 -Wall -Wextra format.cpp -o format
./format
[ 3.14]
A parse that fails quietly
Reading text that is not a number does not throw: the extraction fails, the stream sets its fail bit, and the variable is left at zero. A program that does not check keeps going with a wrong value.
The program reads "abc" into an int initialised to 42 and prints the fail flag and the value. The flag is 1 and the value is 0, which is the state a program has to check before using the number.
The test is if (!input) or if (input.fail()) after the read. Lesson 082 and lesson 081 show how to report the failure to the caller.
#include <iostream>
#include <sstream>
int main()
{
std::istringstream input{"abc"};
int value = 42;
input >> value;
std::cout << input.fail() << ' ' << value << '\n';
}
g++ -std=c++23 -Wall -Wextra fail.cpp -o fail
./fail
1 0
Exercises
- Parse a line of the form "name 42" into a string and an int and check that both extractions succeeded.
- Split a sentence on spaces with getline and count the words.
- Build a line with an ostringstream that prints three numbers in columns of width six.
Questions
A new set of questions in a new order every time. Choose one answer for each, then check.
-
What is a string stream?
Answer
A stream whose source or destination is a string
The same operators work, and the bytes come from or go to a string.
-
Which type reads from text already in memory?
Answer
std::istringstream
The i stands for input, and the source is the string it was built with.
-
What does
operator>>stop at?Answer
Whitespace, for the formatted extraction
The default extraction reads one whitespace-separated token.
-
How do you test whether a parse succeeded?
Answer
Convert the stream to bool after the read
The stream's state becomes false when the extraction fails.
-
What does this program print?
#include <iostream> #include <sstream> #include <string> int main() { std::istringstream input{"46 hello"}; int number = 0; std::string word; input >> number >> word; std::cout << number << ' ' << word; }Answer
46 hello
The extraction reads the int first and the word second, and both succeed.
-
What does this program print?
#include <iostream> #include <sstream> #include <string> int main() { std::ostringstream out; out << "user " << 7 << " has " << 3 << " items " << 3 * 6; std::string text = out.str(); std::cout << text << '\n' << text.size(); }Answer
user 7 has 3 items 18 / 21
The stream builds the line with the values, and the string is twenty-one characters long.
-
What does this program print?
#include <iostream> #include <sstream> #include <string> #include <vector> int main() { std::string line = "one,two,,three"; std::istringstream input{line}; std::vector<std::string> parts; std::string part; while (std::getline(input, part, ',')) parts.push_back(part); for (const auto& p : parts) std::cout << '[' << p << ']'; std::cout << ' ' << parts.size(); }Answer
[one][two][][three] 4
The delimiter split keeps the empty field, and the four parts are printed.
-
What does this program print?
#include <iomanip> #include <iostream> #include <sstream> int main() { std::ostringstream out; out << std::fixed << std::setprecision(2) << std::setw(8) << 3.14159; std::cout << '[' << out.str() << ']'; }Answer
[ 3.14]
The width pads the number to eight characters, and the precision keeps two decimals.
-
What does this program print?
#include <iostream> #include <sstream> #include <string> int main() { std::istringstream input{"42"}; int value = 0; input >> value; std::cout << static_cast<bool>(input) << ' '; std::istringstream bad{"x"}; bad >> value; std::cout << static_cast<bool>(bad); }Answer
1 0
The first extraction succeeds and the second fails, which the stream state reports.
-
Why build a line in an ostringstream instead of printing it piece by piece?
Answer
The text can be returned, compared and stored before it is printed
The stream gives a string value, which a function can return.
The code on this page was built with g++ 15.2.0 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container.
Article author: Arthur Isaev