Safe C++ Way
You can define a function for this using std::istringstream
:
#include <sstream>
bool is_double(std::string const& str) {
std::istringstream ss(str);
// always keep the scope of variables as close as possible. we see
// 'd' only within the following block.
{
double d;
ss >> d;
}
/* eat up trailing whitespace if there was a double read, and ensure
* there is no character left. the eof bit is set in the case that
* `std::ws` tried to read beyond the stream. */
return (ss && (ss >> std::ws).eof());
}
To assist you in figuring out what it does (some points are simplified):
- Creation of a input-stringstream initialized with the string given
- Reading a double value out of it using
operator>>
. This means skipping whitespace and trying to read a double.
- If no double could be read, as in
abc
the stream sets the fail-bit. Note that cases like 3abc
will succeed and will not set the fail-bit.
- If the fail-bit is set,
ss
evaluates to a zero value, which means false.
- If an double was read, we skip trailing whitespace. If we then are at the end of the stream (note that
eof()
will return true if we tried to read past the end. std::ws
does exactly that), eof
will return true. Note this check makes sure that 3abc
will not pass our check.
- If both cases, right and left of the
&&
evaluate to true, we return true to the caller, signaling the given string is a double.
Similar, you check for int
and other types. If you know how to work with templates, you know how to generalize this for other types as well. Incidentally, this is exactly what boost::lexical_cast
provides to you. Check it out: http://www.boost.org/doc/libs/1_37_0/libs/conversion/lexical_cast.htm.
C Way One
This way has advantages (being fast) but also major disadvantages (can't generalized using a template, need to work with raw pointers):
#include <cstdlib>
#include <cctype>
bool is_double(std::string const& s) {
char * endptr;
std::strtod(s.c_str(), &endptr);
if(endptr != s.c_str()) // skip trailing whitespace
while(std::isspace(*endptr)) endptr++;
return (endptr != s.c_str() && *endptr == '\0');
}
strtod
will set endptr
to the last character processed. Which is in our case the terminating null character. If no conversion was performed, endptr is set to the value of the string given to strtod
.
C Way Two
One might thing that std::sscanf
does the trick. But it's easy to oversee something. Here is the correct way to do it:
#include <cstdio>
bool is_double(std::string const& s) {
int n;
double d;
return (std::sscanf(s.c_str(), "%lf %n", &d, &n) >= 1 &&
n == static_cast<int>(s.size()));
}
std::sscanf
will return the items converted. Although the Standard specifies that %n
is not included in that count, serveral sources contradict each other. It's the best to compare >=
to get it right (see the manpage of sscanf
). n
will be set to the amount of the processed characters. It is compared to the size of the string. The space between the two format specifiers accounts for optional trailing whitespace.
Conclusion
If you are a beginner, read into std::stringstream
and do it the C++ way. Best not mess with pointers until you feel good with the general concept of C++.