What Exactly Does Stringstream Do

What exactly does stringstream do?

Sometimes it is very convenient to use stringstream to convert between strings and other numerical types. The usage of stringstream is similar to the usage of iostream, so it is not a burden to learn.

Stringstreams can be used to both read strings and write data into strings. It mainly functions with a string buffer, but without a real I/O channel.

The basic member functions of stringstream class are

  • str(), which returns the contents of its buffer in string type.

  • str(string), which set the contents of the buffer to the string argument.

Here is an example of how to use string streams.

ostringstream os;
os << "dec: " << 15 << " hex: " << std::hex << 15 << endl;
cout << os.str() << endl;

The result is dec: 15 hex: f.

istringstream is of more or less the same usage.

To summarize, stringstream is a convenient way to manipulate strings like an independent I/O device.

FYI, the inheritance relationships between the classes are:

string stream classes

What's the point of stringstream?

Well, one problem is that you cannot "concatenate a bunch of variables in a string using the + operator" (only other strings or char*s).

So, how are you going to turn all your objects into strings? Unlike Java, C++ does not have any to_string() member convention. On the other hand, every class interested in using iostream will define stream inserters (std::ostream& operator<<(std::ostream& os, const MyClass& foo) and maybe std::istream& operator>>(std::istream& os, MyClass& foo).)

So, you use the stream inserters to convert objects to text. Sometimes you don't want to write to the console or to a file, but instead you want to store it as a string.

Also, using the iostream framework lets you use the manipulators to control precision, width, numerical base, and so on, instead of trying to do all that manually as you construct a string.

Now, that's not to say that the stringstream solution is ideal: in fact, a lot of libraries exist to do the same sort of task better (including at least Boost.Format, Boost.Convert, Boost.Lexical_Cast, and Boost.Spirit just in Boost.)

Understanding stringstream

There's a constructor for std::stringstream that takes a std::string as a parameter and initializes the stream with that value.

#include <iostream>
#include <sstream>
#include <string>

int main() {

std::stringstream ss("foo bar");

std::string str1, str2;
ss >> str1 >> str2;

std::cout << "str1: " << str1 << std::endl;
std::cout << "str2: " << str2 << std::endl;

}

This code initializes a stringstream, ss, with the value "foo bar" and then reads it into two strings, str1 and str2, in the same way in which you would read from a file or std::cin.

How does stringstream works with operators and ?

Mixing input and output like this is problematic. The initial extraction of hour sets the eofbit, which means when you try to extract min it immediately fails.

You can add ss.clear() to reset the flags.

void timeConversion(std::string s) 
{
int hour,min;

std::stringstream ss;
ss << s.substr(0,2);
ss >> hour;
std::cout << hour << std::endl;
ss.clear();

ss << s.substr(3,2);
ss >> min;
std::cout << min << std::endl;
}

Or you use separate streams

void timeConversion(std::string s) 
{
int hour,min;

std::stringstream { s.substr(0,2) } >> hour;
std::stringstream { s.substr(3,2) } >> min;

std::cout << hour << std::endl << min << std::endl;
}

Or you could use a different conversion function

void timeConversion(std::string s) 
{
int hour = std::stoi(s.substr(0,2));
int min = std::stoi(s.substr(3,2));

std::cout << hour << std::endl << min << std::endl;
}

Aside: I'd recommend using std::string_view to avoid copying segments of strings, it has the same methods as a const std::string

What's the difference between istringstream, ostringstream and stringstream? / Why not use stringstream in every case?

Personally, I find it very rare that I want to perform streaming into and out of the same string stream.

Usually I want to either initialize a stream from a string and then parse it; or stream things to a string stream and then extract the result and store it.

If you're streaming to and from the same stream, you have to be very careful with the stream state and stream positions.

Using 'just' istringstream or ostringstream better expresses your intent and gives you some checking against silly mistakes such as accidental use of << vs >>.

There might be some performance improvement but I wouldn't be looking at that first.

There's nothing wrong with what you've written. If you find it doesn't perform well enough, then you could profile other approaches, otherwise stick with what's clearest. Personally, I'd just go for:

std::string stHehe( "Hello stackoverflow.com!" );

How to add string to stringstream?

stringstream has operator<< overload to insert data into the stream. It would return a reference to the stream itself so you can chain multiple insertions.

ss << str_one << str_two;
std::cout << ss.str(); // hello world!

As an alternate, you can leverage fold expression(since C++17) to concatenate multiple strings.

template<typename ...T>
std::string concat(T... first){
return ((first+ ", ") + ...);
}

int main(){
std::string a = "abc", b = "def", c = "ghi", d = "jkl";
std::cout << concat(a, b, c, d); // abc, def, ghi,
}

The fold expression is expanded as below:

"abc" + ("def" + ("ghi" + "jkl"));

Demo

Stringstream weird behaviour

As we know now, + is a valid token for a double, so you need a way to skip to the next space-separated token instead of just getting rid of it. This function can do it for you:

template<class Ct>
std::basic_istream<Ct>& next_token(std::basic_istream<Ct>& is) {
is.clear();
std::ctype<Ct> const& ctype = std::use_facet<std::ctype<Ct>>(is.getloc());
if (ctype.is(ctype.space, is.peek())) {
return is >> std::ws;
}
Ct c;
while (is.get(c) && !ctype.is(ctype.space, c)) {
;
}
return is;
}

Then you can change your code to:

stringstream sso("12 + 1442 nana 7676");
double num = 0;
while (sso) {
if (!(sso >> num)) {
sso >> next_token;
} else {
cout << num << endl;
}
}

Output:

12
1442
7676

Why does stringstream change value of target on failure?

From this reference:

If extraction fails (e.g. if a letter was entered where a digit is expected), value is left unmodified and failbit is set (until C++11)

If extraction fails, zero is written to value and failbit is set. If extraction results in the value too large or too small to fit in value, std::numeric_limits::max() or std::numeric_limits::min() is written and failbit flag is set. (since C++11)

It seems that your compiler is compiling in C++11 mode, which changes the behavior.


The input operator uses the locale facet std::num_get whose get function invokes do_get. For C++11 it's specified to use std::strtoll et. al. type of functions. Before C++11 it apparently used std::scanf style parsing (going by the reference, I don't have access to the C++03 specification) to extract the numbers. The change in behavior is due to this change in parsing the input.



Related Topics



Leave a reply



Submit