Reputation:
I tried to perform a string to int conversion as follows:
#include<iostream>
#include<sstream>
#include<string>
int main(int argc, char ** argv)
{
int i;
char *str = "12a3";
std::stringstream ss;
ss << str;
ss >> i;
std::cout << i << std::endl; //prints 12
}
But the resul wasn't one that I expected. I'm a Java so honestly I was waiting for some exception to be thrown. But it wasn't. Is it possible to perform such casting in a safe-way without the legacy atoi
function.
Note, that I'm in C++0x
therefor cannot use std::stoi
.
Upvotes: 0
Views: 457
Reputation: 9434
In the comments we concluded that your goal is to determine whether a string consists solely of a valid representation of a number.
C, and by extension C++ has always focused on stream I/O and therefore stream-like numeric conversions. The consequence of that is that scanf
, istream >>
, atoi
, and strol
all stop at the first character that is NOT part of a valid number.
You goal then is to determine whether the input is fully consumed, or if there are trailing "garbage" characters. The simplest approach is to use strtol which will return a pointer to the first unconsumed character.
char * endptr;
long i = strtol (str, &endptr, 10);
bool valid = *endptr == '\0';
This will still fail if your input looks like this: " 123 ", because the scan will stop at the space character following the '3'.
If your requirements are that trailing spaces are acceptable, your code will need to examining the string that begins at *endptr after the call, to determin if it is an acceptable suffix, or it could trim the input string before doing the test.
Upvotes: 0
Reputation: 25647
When the operator >>
finds a result that it can't use, it sets failbit
on the stream, and the stream itself will evaluate as false
:
if(!(ss >> i)) { // or ss >> i; if(!ss) or if(ss.fail())
// the conversion failed, do whatever you need to here.
ss.clear(); // reset the fail flag so you can read from the stream again
// you can use ss.ignore() to skip bad input if you want to move on to other parts
}
Upvotes: -1
Reputation: 1822
You need to test both unsuccessful stream reading and incomplete stream reading:
ss >> i;
if (!ss || !ss.eof()) {
throw std::invalid_argument("Could not convert to int");
}
Upvotes: 0
Reputation: 33931
Simplest approach I know of.
int main(int argc, char ** argv)
{
std::string num;
long i;
char *str = "12a3";
char * endp;
std::stringstream ss;
ss << str;
ss >> num;
i = strtol(num.c_str(), // string to convert as a C style string,
&endp, //pointer to be updated with character that ended the conversion
10); //base of number conversion
if (*endp != '\0')
{
//didn't read whole string. conversion failed
}
std::cout << i << std::endl; //prints 12
}
Upvotes: 0
Reputation: 27133
This is the correct behaviour. The operation to >>
into an int
succeeded, and this is correct.
You have to ask a different question. The test you really want to perform is: "after I successfully read an int
, are there any more characters which are leftover in the stream and have not yet been processed?"
Consider cin
. When you do
int x, y;
std::cin >> x;
std::cin >> y;
it reads as much as it can from standard input into x
, stopping at the first non-digit. Then, if there is just some whitespace after the first number, then it will attempt to read a number after the whitespace and store that number in y
.
All input streams (whether cin
or an instance stringstream
) are expected to contain many strings/numbers/whatever. Each >>
simply reads as much as it can (successfully).
Upvotes: 4