Given a string that should represent a number, I'd like to put it into a conversion function which would provide notification if the whole string did not convert.
For input: "12"
:
istringstream::operator>>
outputs 12
atoi
outputs 12
stoi
outputs 12
For input "1X"
I'd like a failure response but I get:
istringstream::operator>>
outputs 1
atoi
outputs 1
stoi
outputs 1
For input "X2"
:
istringstream::operator>>
outputs 0 and sets an error flag
atoi
outputs 0
stoi
throws an error
[Live Example]
Is there a way to provoke the error behavior on input "1X"
?
For a given string str
there are several ways to accomplish this each with advantages and disadvantages. I've written a live example here: https://ideone.com/LO2Qnq and discuss each below:
strtol
As suggested here strtol
's out-parameter can be used to get the number of characters read. strtol
actually returns a long
not an int
so a cast is happening on the return.
char* size;
const int num = strtol(i.c_str(), &size, 10);
if(distance(i.c_str(), const_cast<const char*>(size)) == i.size()) {
cout << "strtol: " << num << endl;
} else {
cout << "strtol: error\n";
}
Note that this uses i.c_str()
to refer to the same string. c_str
Returns pointer to the underlying array serving as character storage not a temporary if you have C++11:
c_str()
and data()
perform the same function
Also note that the pointer returned by c_str
will be valid between the strtol
and distance
calls unless:
- Passing a non-
const
reference to the string
to any standard library function
- Calling non-
const
member functions on the string
, excluding operator[]
, at()
, front()
, back()
, begin()
, rbegin()
, end()
and rend()
If you violate either of these cases you'll need to make a temporary copy of i
's underlying const char*
and perform the test on that.
sscanf
sscanf
can use %zn
to return the number of characters read which may be more intuitive than doing a pointer comparison. If base is important, sscanf
may not be a good choice. Unlike strtol
and stoi
which support bases 2 - 36, sscanf
provides specifiers for only octal (%o
), decimal (%d
), and hexadecimal (%x
).
size_t size;
int num;
if(sscanf(i.c_str(), "%d%zn", &num, &size) == 1 && size == i.size()) {
cout << "sscanf: " << num << endl;
} else {
cout << "sscanf: error\n";
}
stoi
As suggested here stoi
's output parameter works like sscanf
's %n
returning the number of characters read. In keeping with C++ this takes a string
and unlike the C implementations above stoi
throws an invalid_argument
if the first non-whitespace character is not considered a digit for the current base, and this unfortunately means that unlike the C implementations this must check for an error in both the try
and catch
blocks.
try {
size_t size;
const auto num = stoi(i, &size);
if(size == i.size()) {
cout << "stoi: " << num << endl;
} else {
throw invalid_argument("invalid stoi argument");
}
} catch(const invalid_argument& /*e*/) {
cout << "stoi: error\n";
}
Alternatively you can use std::istringstream
as you mentioned, but check to make sure it parsed to the end of the stream. Assuming you have a constant reference, you could do something like the following
T parse(const std::string& input) {
std::istringstream iss(input);
T result;
iss >> result;
if (iss.eof() || iss.tellg() == int(input.size())) {
return result;
} else {
throw std::invalid_argument("Couldn't parse entire string");
}
}
The benefit of this approach is that you parse anything that overloads operator>>
. Note: I'm not entirely sure if the condition is enough, but with my testing it seemed to be. For some reason the stream would get a failure marking if it parsed to the end.