How to determine if a string is a number with C++?
I've had quite a bit of trouble trying to write a function that checks if a string is a number. For a game I am writing I just need to check if a line from the file I am reading is a number or not (I will know if it is a parameter this way). I wrote the below function which I believe was working smoothly (or I accidentally edited to s开发者_运维问答top it or I'm schizophrenic or Windows is schizophrenic):
bool isParam (string line)
{
if (isdigit(atoi(line.c_str())))
return true;
return false;
}
The most efficient way would be just to iterate over the string until you find a non-digit character. If there are any non-digit characters, you can consider the string not a number.
bool is_number(const std::string& s)
{
std::string::const_iterator it = s.begin();
while (it != s.end() && std::isdigit(*it)) ++it;
return !s.empty() && it == s.end();
}
Or if you want to do it the C++11 way:
bool is_number(const std::string& s)
{
return !s.empty() && std::find_if(s.begin(),
s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
}
As pointed out in the comments below, this only works for positive integers. If you need to detect negative integers or fractions, you should go with a more robust library-based solution. Although, adding support for negative integers is pretty trivial.
Why reinvent the wheel? The C standard library (available in C++ as well) has a function that does exactly this:
char* p;
long converted = strtol(s, &p, 10);
if (*p) {
// conversion failed because the input wasn't a number
}
else {
// use converted
}
If you want to handle fractions or scientific notation, go with strtod
instead (you'll get a double
result).
If you want to allow hexadecimal and octal constants in C/C++ style ("0xABC"
), then make the last parameter 0
instead.
Your function then can be written as
bool isParam(string line)
{
char* p;
strtol(line.c_str(), &p, 10);
return *p == 0;
}
With C++11 compiler, for non-negative integers I would use something like this (note the ::
instead of std::
):
bool is_number(const std::string &s) {
return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
}
http://ideone.com/OjVJWh
You can do it the C++ way with boost::lexical_cast. If you really insist on not using boost you can just examine what it does and do that. It's pretty simple.
try
{
double x = boost::lexical_cast<double>(str); // double could be anything with >> operator.
}
catch(...) { oops, not a number }
I just wanted to throw in this idea that uses iteration but some other code does that iteration:
#include <string.h>
bool is_number(const std::string& s)
{
return( strspn( s.c_str(), "-.0123456789" ) == s.size() );
}
It's not robust like it should be when checking for a decimal point or minus sign since it allows there to be more than one of each and in any location. The good thing is that it's a single line of code and doesn't require a third-party library.
Take out the '.' and '-' if positive integers are all that are allowed.
I'd suggest a regex approach. A full regex-match (for example, using boost::regex) with
-?[0-9]+([\.][0-9]+)?
would show whether the string is a number or not. This includes positive and negative numbers, integer as well as decimal.
Other variations:
[0-9]+([\.][0-9]+)?
(only positive)
-?[0-9]+
(only integer)
[0-9]+
(only positive integer)
Here's another way of doing it using the <regex>
library:
bool is_integer(const std::string & s){
return std::regex_match(s, std::regex("[(-|+)|][0-9]+"));
}
With this solution you can check everything from negative to positive numbers and even float numbers. When you change the type of num
to integer you will get an error if the string contains a point.
#include<iostream>
#include<sstream>
using namespace std;
int main()
{
string s;
cin >> s;
stringstream ss;
ss << s;
float num = 0;
ss >> num;
if(ss.good()) {
cerr << "No Valid Number" << endl;
}
else if(num == 0 && s[0] != '0') {
cerr << "No Valid Number" << endl;
}
else {
cout << num<< endl;
}
}
Prove: C++ Program
I've found the following code to be the most robust (c++11). It catches both integers and floats.
#include <regex>
bool isNumber( std::string token )
{
return std::regex_match( token, std::regex( ( "((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?" ) ) );
}
Try this:
isNumber(const std::string &str) {
return !str.empty() && str.find_first_not_of("0123456789") == string::npos;
}
Here is a solution for checking positive integers:
bool isPositiveInteger(const std::string& s)
{
return !s.empty() &&
(std::count_if(s.begin(), s.end(), std::isdigit) == s.size());
}
Brendan this
bool isNumber(string line)
{
return (atoi(line.c_str()));
}
is almost ok.
assuming any string starting with 0 is a number, Just add a check for this case
bool isNumber(const string &line)
{
if (line[0] == '0') return true;
return (atoi(line.c_str()));
}
ofc "123hello" will return true like Tony D noted.
As it was revealed to me in an answer to my related question, I feel you should use boost::conversion::try_lexical_convert
The simplest I can think of in c++
bool isNumber(string s) {
if(s.size()==0) return false;
for(int i=0;i<s.size();i++) {
if((s[i]>='0' && s[i]<='9')==false) {
return false;
}
}
return true;
}
Working code sample: https://ideone.com/nRX51Y
A solution based on a comment by kbjorklu is:
bool isNumber(const std::string& s)
{
return !s.empty() && s.find_first_not_of("-.0123456789") == std::string::npos;
}
As with David Rector's answer it is not robust to strings with multiple dots or minus signs, but you can remove those characters to just check for integers.
However, I am partial to a solution, based on Ben Voigt's solution, using strtod
in cstdlib to look decimal values, scientific/engineering notation, hexidecimal notation (C++11), or even INF/INFINITY/NAN (C++11) is:
bool isNumberC(const std::string& s)
{
char* p;
strtod(s.c_str(), &p);
return *p == 0;
}
My solution using C++11 regex (#include <regex>
), it can be used for more precise check, like unsigned int
, double
etc:
static const std::regex INT_TYPE("[+-]?[0-9]+");
static const std::regex UNSIGNED_INT_TYPE("[+]?[0-9]+");
static const std::regex DOUBLE_TYPE("[+-]?[0-9]+[.]?[0-9]+");
static const std::regex UNSIGNED_DOUBLE_TYPE("[+]?[0-9]+[.]?[0-9]+");
bool isIntegerType(const std::string& str_)
{
return std::regex_match(str_, INT_TYPE);
}
bool isUnsignedIntegerType(const std::string& str_)
{
return std::regex_match(str_, UNSIGNED_INT_TYPE);
}
bool isDoubleType(const std::string& str_)
{
return std::regex_match(str_, DOUBLE_TYPE);
}
bool isUnsignedDoubleType(const std::string& str_)
{
return std::regex_match(str_, UNSIGNED_DOUBLE_TYPE);
}
You can find this code at http://ideone.com/lyDtfi, this can be easily modified to meet the requirements.
We may use a stringstream class.
bool isNumeric(string str)
{
stringstream stream;
double number;
stream<<str;
stream>>number;
return stream.eof();
}
Using <regex>
. This code was tested!
bool isNumber(const std::string &token)
{
return std::regex_match(token, std::regex("(\\+|-)?[0-9]*(\\.?([0-9]+))$"));
}
to check if a string is a number integer or floating point or so you could use :
#include <sstream>
bool isNumber(string str) {
double d;
istringstream is(str);
is >> d;
return !is.fail() && is.eof();
}
Try this:
bool checkDigit(string str)
{
int n=str.length();
for(int i=0; i < n ; i++)
{
if(str[i]<'0' || str[i]>'9')
return false;
}
return true;
}
C/C++ style for unsigned integers, using range based for
C++11:
int isdigits(const std::string & s)
{
for (char c : s) if (!isdigit(c)) return (0);
return (1);
}
After consulting the documentation a bit more, I came up with an answer that supports my needs, but probably won't be as helpful for others. Here it is (without the annoying return true and return false statements :-) )
bool isNumber(string line)
{
return (atoi(line.c_str()));
}
I think this regular expression should handle almost all cases
"^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"
so you can try the following function that can work with both (Unicode and ANSI)
bool IsNumber(CString Cs){
Cs.Trim();
#ifdef _UNICODE
std::wstring sr = (LPCWSTR)Cs.GetBuffer(Cs.GetLength());
return std::regex_match(sr, std::wregex(_T("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?")));
#else
std::string s = (LPCSTR)Cs.GetBuffer();
return std::regex_match(s, std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"));
#endif
}
include <string>
For Validating Doubles:
bool validateDouble(const std::string & input) {
int decimals = std::count(input.begin(), input.end(), '.'); // The number of decimals in the string
int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string
if (input.size() == decimals + negativeSigns) // Consists of only decimals and negatives or is empty
return false;
else if (1 < decimals || 1 < negativeSigns) // More than 1 decimal or negative sign
return false;
else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character
return false;
else if (strspn(input.c_str(), "-.0123456789") != input.size()) // The string contains a character that isn't in "-.0123456789"
return false;
return true;
}
For Validating Ints (With Negatives)
bool validateInt(const std::string & input) {
int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string
if (input.size() == negativeSigns) // Consists of only negatives or is empty
return false;
else if (1 < negativeSigns) // More than 1 negative sign
return false;
else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character
return false;
else if (strspn(input.c_str(), "-0123456789") != input.size()) // The string contains a character that isn't in "-0123456789"
return false;
return true;
}
For Validating Unsigned Ints
bool validateUnsignedInt(const std::string & input) {
return (input.size() != 0 && strspn(input.c_str(), "0123456789") == input.size()); // The string is not empty and contains characters only in "0123456789"
}
bool isNumeric(string s){
if ( !s.empty() && s[0] != '-' )
s = "0" + s; //prepend 0
string garbage;
stringstream ss(s);
ss >> *(auto_ptr<double>(new double)) >> garbage;
/*
//the line above extracts the number into an anonymous variable. it could also be done like this:
double x;
ss >> x >> garbage;
*/
//if there is no garbage return true or else return false
return garbage.empty();
}
how it works: the stringstream >> overload can convert strings to various arithmetic types it does this by reading characters sequentially from the stringstream (ss in this case) until it runs out of characters OR the next character does not meet the criteria to be stored into the destination variable type.
example1:
stringstream ss("11");
double my_number;
ss >> my_number; //my number = 11
example2:
stringstream ss("011");
double my_number;
ss >> my_number; //my number = 11
example3:
stringstream ss("11ABCD");
double my_number;
ss >> my_number; //my number = 11 (even though there are letters after the 11)
the "garbage" variable explanation":
why not just check if extraction into my double has a valid value and then return true if it does?
notice example3 above will still successfully read the number 11 into the my_number variable even if the input string is "11ABCD" (which is not a number).
to handle this case we can do another extraction into a string variable(which I named garbage) which can read anything that may have been left over in the string buffer after the initial extraction into the variable of type double. If anything is left over it will be read into "garbage" which means the full string passed in was not a number (it just begins with one). in this which case we'd want to return false;
the prepended "0" explanation":
attempting to extract a single character into a double will fail(returning 0 into our double) but will still move the string buffer position to after the character. In this case our garbage read will be empty which would cause the function to incorrectly return true. to get around this I prepended a 0 to the string so that if for example the string passed in was "a" it gets changed to "0a" so that the 0 will be extracted into the double and "a" gets extracted into garbage.
prepending a 0 will not affect the value of the number so the number will still be correctly extracted into our double variable.
Yet another answer, that uses stold
(though you could also use stof
/stod
if you don't require the precision).
bool isNumeric(const std::string& string)
{
std::size_t pos;
long double value = 0.0;
try
{
value = std::stold(string, &pos);
}
catch(std::invalid_argument&)
{
return false;
}
catch(std::out_of_range&)
{
return false;
}
return pos == string.size() && !std::isnan(value);
}
bool is_number(const string& s, bool is_signed)
{
if (s.empty())
return false;
auto it_begin = s.begin();
if (is_signed && (s.front() == '+' || s.front() == '-'))
++it_begin;
auto non_digit = std::find_if(it_begin, s.end(), [](const char& c) { return !std::isdigit(c); });
return non_digit == s.end();
}
The following regex-based function supports both signed integer literals and decimal literals in standard and scientific notation (e.g. 42
, 3.14
, -1
, +58.
, 4e2
, 1.e-2
, -12.34e-56
, .1E4
). Integers prefixed with zeros are also matched (e.g. 0001
):
#include <string_view>
#include <regex>
bool is_number(std::string_view s) {
static std::regex const re{
R"([-+]?((\.\d+)|(\d+\.)|(\d+))\d*([eE][-+]?\d+)?)"};
return std::regex_match(s.data(), re);
}
Hexadecimal literals, binary literals, single-quote separators and suffixes (f
, F
, l
, L
, u
, U
, ll
, LL
, Z
) are not matched.
Below are some unit tests using gtest. The complete code is available at https://godbolt.org/z/vbTnMx7or
TEST(IsNumber, True) {
char const *tests[]{"42", "3.14", "-0", "+4", ".3",
"+.5", "-.23", "7.", "1e2", "1.e2",
"1.0e-2", "8.e+09", "2E34", "61e2", "-0e1",
"+0E+10", "-.01E-5", "07", "+01E1", "12.34"};
for (auto const &x : tests) {
EXPECT_TRUE(is_number(x));
}
}
TEST(IsNumber, False) {
char const *tests[]{"4e", "xyz", ".3.14", "--0", "2-4",
"..3", ".+5", "7 2", "1f", "1.0f",
"1e-2.0", "8e+0e1", "2E.4", "a", "e15",
"-0e10.3", ".e2", "+1.2E0e", "1.2+3", "e1"};
for (auto const &x : tests) {
EXPECT_FALSE(is_number(x));
}
}
Few months ago, I implemented a way to determine if any string is integer, hexadecimal or double.
enum{
STRING_IS_INVALID_NUMBER=0,
STRING_IS_HEXA,
STRING_IS_INT,
STRING_IS_DOUBLE
};
bool isDigit(char c){
return (('0' <= c) && (c<='9'));
}
bool isHexaDigit(char c){
return ((('0' <= c) && (c<='9')) || ((tolower(c)<='a')&&(tolower(c)<='f')));
}
char *ADVANCE_DIGITS(char *aux_p){
while(CString::isDigit(*aux_p)) aux_p++;
return aux_p;
}
char *ADVANCE_HEXADIGITS(char *aux_p){
while(CString::isHexaDigit(*aux_p)) aux_p++;
return aux_p;
}
int isNumber(const string & test_str_number){
bool isHexa=false;
char *str = (char *)test_str_number.c_str();
switch(*str){
case '-': str++; // is negative number ...
break;
case '0':
if(tolower(*str+1)=='x') {
isHexa = true;
str+=2;
}
break;
default:
break;
};
char *start_str = str; // saves start position...
if(isHexa) { // candidate to hexa ...
str = ADVANCE_HEXADIGITS(str);
if(str == start_str)
return STRING_IS_INVALID_NUMBER;
if(*str == ' ' || *str == 0)
return STRING_IS_HEXA;
}else{ // test if integer or float
str = ADVANCE_DIGITS(str);
if(*str=='.') { // is candidate to double
str++;
str = ADVANCE_DIGITS(str);
if(*str == ' ' || *str == 0)
return STRING_IS_DOUBLE;
return STRING_IS_INVALID_NUMBER;
}
if(*str == ' ' || *str == 0)
return STRING_IS_INT;
}
return STRING_IS_INVALID_NUMBER;
}
Then in your program you can easily convert the number in function its type if you do the following,
string val; // the string to check if number...
switch(isNumber(val)){
case STRING_IS_HEXA:
// use strtol(val.c_str(), NULL, 16); to convert it into conventional hexadecimal
break;
case STRING_IS_INT:
// use (int)strtol(val.c_str(), NULL, 10); to convert it into conventional integer
break;
case STRING_IS_DOUBLE:
// use atof(val.c_str()); to convert it into conventional float/double
break;
}
You can realise that the function will return a 0 if the number wasn't detected. The 0 it can be treated as false (like boolean).
I propose a simple convention:
If conversion to ASCII is > 0 or it starts with 0 then it is a number. It is not perfect but fast.
Something like this:
string token0;
if (atoi(token0.c_str())>0 || isdigit(token0.c_str()[0]) ) { //this is a value
// do what you need to do...
}
精彩评论