Why Does Stringstream ≫≫ Change Value of Target on Failure

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.

c++ stringstream not working properly when changing integer to string

std::ostringstream::str()

(1) string str() const;

(2) void str (const string& s);

The first form (1) returns a string object with a copy of the current contents of the stream.

The second form (2) sets s as the contents of the stream, discarding any previous contents.

To clean up the buffer, use the second overload as well:

xString = ssObject.str();
ssObject.str("");
// ^^

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.)

Why does cin, expecting an int, change the corresponding int variable to zero in case of invalid input?

The behavior you want to observe changed in 2011. Until then:

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

But since C++11:

If extraction fails, zero is written to value and failbit is set. [...]

(From cppr.)

C++ cin reading string into int type returns 0

Not sure why the question got down/close votes as it's clearly stated and not broad (and judging by the comments, many people don't know the answer)...

Anyway, for the code:

int age;
cin >> age;

if the input stream did not contain any digits, then the stream is put into a fail state and age is set to 0. The characters in the input stream remain there; they can be read by a future read operation once the fail state is cleared.

The behaviour of operator>> is summarized on cppreference , the full description in the standard is somewhat complicated.

Note: This changed in C++11; commentors reporting garbage output are either running compilers in pre-C++11 mode, or bugged ones.

Why does filtering out ASCII value 32 (' ') from a stringstream with the following function also filter out '\n' and '\t'?

In this code:

while (ss >> c) {
if (c == ' ') {

The if will NEVER evaluate as true, because operator>> skips leading whitespace by default, which includes spaces, tabs, and line breaks. So c will never be a whitespace character like ' '.

Either:

  • use std::noskipws, like @WhozCraig suggested in comments:
while (ss >> std::noskipws >> c)
  • use std::istream::get(), which does not skip whitespace:
while (ss.get(c))

That being said, there are other bugs in you code.

You are returning the unmodified s instead of the prepared final_string.

It looks like your inner while loop is trying to minimize runs of 2+ spaces into 1 space for output. Which is fine, except that is losing the character that ends a detected run.

Try something more like this:

std::string filter(const std::string &s) {
std::istringstream iss(s);
char c;
std::string final_string;
while (iss.get(c)) {
if (c == ' ') {
while (iss.get(c)) {
if (c != ' ') {
final_string += ' ';
final_string += c;
break;
}
}
}
else {
final_string += c;
}
}
return final_string;
}

Lastly, when building up a new std::string in this manner, it is generally more efficient to use an std::ostringstream instead of operator+= (unless you reserve() the std::string up front), eg:

std::string filter(const std::string &s) {
std::istringstream iss(s);
char c;
std::ostringstream final_string;
while (iss.get(c)) {
if (c == ' ') {
while (iss.get(c)) {
if (c != ' ') {
final_string << ' ' << c;
break;
}
}
}
else {
final_string << c;
}
}
return final_string.str();
}

std::istream to unsigned numerical value, how to detect negative values?

Streams are allowed to take in negative numbers for unsigned types. It has the same mechanics as

unsigned type foo = -some_value

Since they can take in a negative number the stream will never fail and you will have the normal behavior of assigning a negative number to an unsigned type.

We can add a check for this though in your function. For a type T, T() - T(1) < 0, will only be true if the type is signed, otherwise the subtraction would wrap around and become the largest value T can represent. So, if we check that condition, and the string starts with a '-', then you know it is not a "valid" value. That makes you function look like

template<typename T>
void checkValid( const std::string& val )
{
std::stringstream str1;
T temp1;

str1 << val;
str1 >> temp1;
if ( str1.fail() || str1.bad() || (!(T() - T(1) < T()) && val[0] == '-') )
std::cout << "error, " << val << " is not a valid string value" << std::endl;
else
std::cout << "ok, " << val << " is converted to " << temp1 << std::endl;
}

If your string can have leading whitespace then you will want to replace the val[0] == '-' check with something like val[val.find_first_not_of(" ")] == '-'

How do you get the remaining string after a string extracted from a sstream variable?

You can use std::getline to get the rest of the string from the stream:

#include <iostream>
#include <sstream>

using namespace std;

int main() {
stringstream ss("abc gg rrr ff");
string s1, s2;
ss >> s1;
getline(ss, s2); //get rest of the string!
cout << s1 << endl;
cout << s2 << endl;
return 0;
}

Output:

abc
gg rrr ff

Demo : http://www.ideone.com/R4kfV

There is an overloaded std::getline function in which a third parameter takes a delimiter upto which you can read the string. See the documentation of std::getline:

  • std::getline


Related Topics



Leave a reply



Submit