sstream
Header providing std::istringstream, std::ostringstream, and std::stringstream for in-memory formatted I/O on std::string buffers.
<sstream>since C++98The <sstream> header provides stream classes that perform formatted I/O against an in-memory std::string buffer rather than a file or terminal.
Overview
<sstream> exposes three concrete class templates and their wchar_t counterparts:
| Class template | Direction | Base |
|---|---|---|
std::basic_istringstream<CharT> | read-only | std::basic_istream<CharT> |
std::basic_ostringstream<CharT> | write-only | std::basic_ostream<CharT> |
std::basic_stringstream<CharT> | read-write | std::basic_iostream<CharT> |
The char specialisations β std::istringstream, std::ostringstream, std::stringstream β cover the vast majority of use cases. Wide variants (std::wistringstream, etc.) follow the same interface with wchar_t as the character type.
The underlying buffer is a std::basic_stringbuf<CharT>. Every stream operation that works on std::cout or std::cin works identically here: format flags, manipulators, operator<<, operator>>, getline, and locale-sensitive conversions all apply.
str() member exists in two forms:
str() constβ returns the current buffer contents as astd::string.str(const std::string&)β replaces the buffer and resets the stream position.
Since C++20, additional overloads accept rvalue references and std::string_view.
When to prefer alternatives: For straightforward value formatting, std::format (C++20, <format>) is faster, type-safe, and has no mutable state. For high-performance scalar β string conversion in tight loops, std::to_chars / std::from_chars (C++17, <charconv>) avoids locale overhead and heap allocation entirely.
Syntax
#include <sstream>
// Write values into a string
std::ostringstream oss;
oss << value;
std::string result = oss.str();
// Parse values from a string
std::istringstream iss(source);
iss >> value;
// Round-trip: write then read back from the same buffer
std::stringstream ss;
ss << value;
ss.seekg(0); // rewind get pointer before reading
ss >> value;Examples
Formatted string construction
#include <sstream>
#include <iomanip>
#include <string>
std::string format_hex_color(unsigned r, unsigned g, unsigned b) {
std::ostringstream oss;
oss << '#'
<< std::hex << std::uppercase << std::setfill('0')
<< std::setw(2) << r
<< std::setw(2) << g
<< std::setw(2) << b;
return oss.str();
}
// format_hex_color(255, 128, 0) β "#FF8000"Since C++20 this collapses to std::format("#{:02X}{:02X}{:02X}", r, g, b).
Parsing whitespace-delimited tokens
#include <sstream>
#include <vector>
#include <string>
std::vector<int> parse_ints(const std::string& line) {
std::istringstream iss(line);
std::vector<int> out;
int x;
while (iss >> x) {
out.push_back(x);
}
return out;
}
// parse_ints("10 -3 42 7") β {10, -3, 42, 7}operator>> skips leading whitespace by default and sets failbit on a type mismatch, so the loop terminates cleanly on a non-integer token or end-of-string.
Parsing structured lines
The canonical pattern for delimiter-separated fields is std::getline into an istringstream:
#include <fstream>
#include <sstream>
#include <string>
void process_tsv(std::istream& file) {
std::string line;
while (std::getline(file, line)) {
std::istringstream row(line);
std::string timestamp, level, message;
std::getline(row, timestamp, '\t');
std::getline(row, level, '\t');
std::getline(row, message);
// ...
}
}Custom type round-trip
std::stringstream is the standard mechanism for verifying that operator<< and operator>> form a symmetric pair:
#include <sstream>
#include <cassert>
struct Vec2 { double x, y; };
std::ostream& operator<<(std::ostream& out, const Vec2& v) {
return out << v.x << ' ' << v.y;
}
std::istream& operator>>(std::istream& in, Vec2& v) {
return in >> v.x >> v.y;
}
void roundtrip_test() {
const Vec2 original{3.14, -2.71};
std::stringstream ss;
ss << original;
Vec2 recovered{};
ss >> recovered;
assert(recovered.x == original.x && recovered.y == original.y);
}Note: no seekg(0) is required here. A default-constructed stringstream starts with the get pointer at position 0, so extraction immediately follows insertion without a seek.
Reusing a stream object in a loop
Constructing an ostringstream inside a loop allocates a new stringbuf each iteration. Hoist the object and reset it instead:
#include <sstream>
#include <vector>
#include <string>
std::vector<std::string> to_strings(const std::vector<double>& values) {
std::ostringstream oss;
std::vector<std::string> out;
out.reserve(values.size());
for (double v : values) {
oss.str(""); // clear buffer content
oss.clear(); // clear eofbit/failbit β both calls are required
oss << v;
out.push_back(oss.str());
}
return out;
}Locale-sensitive parsing
String streams inherit the global locale by default. Override it with imbue() for controlled locale behaviour:
#include <sstream>
#include <locale>
double parse_german(const std::string& s) {
std::istringstream iss(s);
iss.imbue(std::locale("de_DE.utf8"));
double value{};
iss >> value;
return value;
}
// parse_german("1.234,56") β 1234.56Chrono parsing (C++20)
std::chrono::parse reads from any std::istream, making istringstream the natural adapter for timestamp strings:
#include <sstream>
#include <chrono>
std::chrono::sys_seconds parse_timestamp(const std::string& s) {
std::istringstream iss(s);
std::chrono::sys_seconds tp;
iss >> std::chrono::parse("%Y-%m-%d %H:%M:%S", tp); // C++20
if (iss.fail()) throw std::runtime_error("bad timestamp: " + s);
return tp;
}Best Practices
Use <format> for pure output (C++20). std::format is stateless, does not allocate a stringbuf, and compiles format strings at compile time. Prefer it for any new code where you control the format string.
Use <charconv> for scalar conversion in hot paths (C++17). std::to_chars and std::from_chars are locale-independent, exception-free, and operate directly on a char buffer. They are routinely 5β10Γ faster than the stream equivalent for numeric conversions.
Pin the locale when output must be portable. Calling oss.imbue(std::locale::classic()) on construction prevents the global locale from injecting commas or non-ASCII decimal separators into numeric output.
Call str() once and store the result. Each invocation copies the internal buffer. Store oss.str() in a local variable rather than calling it multiple times.
Common Pitfalls
Forgetting clear() when reusing. str("") empties the buffer but does not touch the stream state flags. If the stream reached EOF or a parse error, eofbit or failbit remain set and all subsequent extractions silently fail:
std::istringstream iss("42");
int x;
iss >> x; // x == 42, eofbit set
iss.str("99"); // buffer replaced, eofbit still set
iss >> x; // FAILS silently β x unchanged
iss.clear(); // now clear error state
iss >> x; // x == 99Reading from a written stringstream without seeking. After writing to a stringstream, the get pointer sits at the write position. Extraction from that point reads nothing:
std::stringstream ss;
ss << "hello";
std::string s;
ss >> s; // s is empty β get pointer is at end
ss.seekg(0); // rewind
ss >> s; // s == "hello"Alternatively, construct a fresh std::istringstream(ss.str()) for a clean read.
Constructing istringstream from a string and then seeking. When you construct std::istringstream iss(src), the get pointer starts at position 0 automatically. An initial seekg(0) is a no-op; it does not cause harm but is unnecessary noise.
Assuming locale independence without enforcing it. If user code ever calls std::locale::global(some_locale), all subsequently constructed string streams will use that locale. For serialization or protocol output where locale must not vary, always imbue(std::locale::classic()) explicitly.
See Also
<charconv>β locale-independent, zero-allocationstd::to_chars/std::from_chars(C++17)<format>β type-safe formatted string construction viastd::format(C++20)<fstream>β file-backed streams sharing the sameoperator<</>>interface<iomanip>β format manipulators:setw,setfill,hex,fixed,setprecision<ios>β stream state flags (goodbit,eofbit,failbit,badbit) andclear()