-
Notifications
You must be signed in to change notification settings - Fork 1
2.1 checking whether a string is a valid number
Use boost::lexical_cast and catch the failed conversion exception:
#include <boost/lexical_cast.hpp>
std::string num;
bool is_valid = true;
try {
int result = boost::lexical_cast<int>(num); // or your choice of number type
} catch (boost::bad_lexical_cast const&) {
is_valid = false;
}
The Boost lexical_cast
function template uses the standard library's formatted input functions to perform the conversion, and throws bad_lexical_cast
in case of failure.
This approach will work, but is not ideal, for several reasons:
- The recommended approach for using exceptions is to allow them to propagate to the point where they are meaningful, and to catch them there. For example, the caller of the caller of the current function may be in charge of error handling, and therefore the
catch
block belongs there. - This conversion method does not test whether the input string may be represented within the range of the supplied type.
- This approach is not generic; that is, the type of the input and result are fixed as
std::string
andint
, respectively.
If the user desires to know whether the string is a number of some sort, instead of a specific C++ type, a different approach may be required.
Here is my take on it:
Assumptions:
- Any integer values are in the range [LONG_MIN,ULONG_MAX] will return true
- Non-integer values are in base 10 decimal form or exponential notation will return true
- Decimal values have decimal digits <= LDBL_DIG
- Decimal values are in the range [LDBL_MIN ,LDBL_MAX]
- Space prefixed numeric values return true, space suffixed numeric values return false (need to trim string first)
Basic Idea:
- Use std::stringstream because it already defines this conversion
Challenges:
-
Didn’t want std::stringstream to throw
Turned off conversion exceptions and checked for good conversion.
-
What type should we convert too?
- May be integer value that only fits in an unsigned long
- May be negative integer value that only fits in a long
- May be very small negative decimal value
- May be very small positive decimal value
- May be very large positive decimal value
Attempted multiple conversions with perceived most common usage first then to less common usage until conversion is successful.
-
if the first characters are numeric the conversion will still succeed so need to check for eof to avoid “10 Dollars” being a reported as a valid number.
Code:
template <typename T>
bool StringToNumber ( const std::string& text, T& ret )
{
std::istringstream ss(text);
// ignore white space prefix
ss.setf(std::ios::skipws);
// turn off exceptions
ss.exceptions(0);
// attempt conversion extraction
ss >> ret;
// make sure we used the entire string and did not fail
if(ss.eof() && !ss.fail()) return true;
return false;
}
bool isNumber(const std::string &text)
{
unsigned long intResult;
//First check for integer values
if(StringToNumber(text, intResult)) return true;
// Next check for negative possibilities
long intNegResult;
if(StringToNumber(text, intNegResult)) return true;
// Try a long double
long double dblResult;
if(StringToNumber(text, dblResult)) return true;
return false;
}
Since I turned off exceptions, the stringsream should not throw when encountering an invalid character when attempting to convert to a numeric type. I tested with the following test cases: Test Cases:
- zero
0, 0.0, -0, +0, -0.0, +0.0
- valid small positive integers
1, 99
- valid large negative integers
-1, -88
- valid large (positive) integer
ULONG_MAX
- valid small (negative) integer
LONG_MIN
- valid small positive decimal value
1 - LDBL_EPSILON (this one didn't work as I expected)
- valid small negative decimal value
LDBL_MIN
- valid large decimal value
LDBL_MAX
- space padded
“ 0 “, “ 1 “, “99 “, " 99", “ .0”, “0.123456789 ”,
- invalid numeric value with multiple signs
- invalid numeric value with multiple decimal
- all text
- all space
- empty string
- numeric value followed by text