194

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 stop it or I'm schizophrenic or Windows is schizophrenic):

bool isParam (string line)
{
    if (isdigit(atoi(line.c_str())))
        return true;

    return false;
}
10
  • 257
    I hate seeing if (expr) return true; return false;! Just write return expr;. Commented Jan 11, 2011 at 6:08
  • 31
    @ephemient My style is to do the same as you. But is it really a big deal? Commented Jan 11, 2011 at 6:12
  • 5
    Yeah. I have a bad habit of coding long-style when learning a new language. I am new to C++ and more hesitant to "shortcuts" (or perceived shortcuts). Commented Jan 11, 2011 at 6:59
  • 79
    @Brennan Vincent: Yes, it's a big deal. It's the same class of mistakes as if (expr) return expr; else return expr; , if (expr == true) , (if expr != false), or if ((expr == true) == true). They all introduce complexity that does not benefit the writer, reader, or compiler of the code. The elimination of needless complexity is not a shortcut; it's key to writing better software. Commented Jan 11, 2011 at 8:24
  • 8
    @MSalters Personally I do not think the OP's version is necessarily that bad.I use this construct when I suspect there might be more code between the if statement and the return statement. Only when I know for sure there is not going to be anything else, I collapse it to return expr. Commented Mar 18, 2019 at 11:29

41 Answers 41

207

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.

Sign up to request clarification or add additional context in comments.

17 Comments

Also doesn't handle negative numbers and non-whole numbers. We can't know what the requirements are based on the question.
You could also use !s.empty() && s.find_first_not_of("0123456789") == std::string::npos; for a C++03 one-liner.
Also doesn't handle decimal numbers eg: 1.23
@Remy Lebeau, yes it does. It's not actually converting the string to an int. It's just identifying whether a string is composed of numeric digits. It doesn't matter how long the string is.
Don't forget to include <string> <algorithm> and <cctype> to make the C++11 example work.
|
135

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;
}

22 Comments

This function discards white space in front. You thus have to check first char for isdigit.
@chmike: Based on my understanding of the question, discarding leading whitespace is the correct behavior (atoi as used in the question does this also).
The question didn't explicitly specify it, but my understanding of the requirement "checks if a string is a number" means that the whole string is the number, thus no spaces. I felt the need to point out that your answer is different to the others in this regard. Your answer may be fine if it's ok for the string to have spaces in front of the number.
@BenVoigt You're saying that p will be set to nullptr if strtol is sucessful, right? That's not what I'm seeing :(
@JonathanMee: No, p will be pointing to the NUL that terminates the string. So p != 0 and *p == 0.
|
59

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

2 Comments

If there are utf8 characters in the string, you would get run time error.
@LionKing Hopefully a runtime error. Technically speaking you get undefined behavior, which could be even worse I suppose. This type of use of isdigit is explicitly discouraged in en.cppreference.com/w/cpp/string/byte/isdigit
29

Use the std library functions like std::stoi now instead of lexical_cast.


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 }

15 Comments

Using try{} catch{} a good idea? Should we not avoid it as much as possible?
try{} catch{} is appropriate here. However, catch(...) is just bad practice. In this case, use boost::bad_lexical_cast for your exception handler.
I feel like this is trying to read from a file. NO matter how much checking you do on the file you will not know if it's possible to read from it until you do it. It's either going to work or not. In which case you are going to need to catch an exception. So in this case I think this is a completely fine way of doing it.
@EarlGray - I would certainly be interested in hearing what OS dependent actions windows would take. The standard is quite clear about how this code should behave.
|
22

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.

10 Comments

error: 'strspn' was not declared in this scope I think this is because I am missing a "#include" but what one
If you're going to use std::string, use its find_first_not_of member function.
This would fail if you pass in a string of "12.3-4.55-", which is obviously not a valid number
Buzzrick, the proposed answer already states that this would fail with the non-number you mentioned.
if you limit it only to "0123456789" then the formula is perfect to test for unsigned integer
|
19

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)

3 Comments

Ahem, I tried to use std::regex with gcc 4.7, gcc 4.8 - they both throw std::regex_error on any sign of [ in regexp, even for an innocent "[abc]" (do I do that wrong?). clang-3.4 is not aware of <regex> at all. Anyway, this seems to be the sanest answer, +1.
@EarlGray: Regex is only available properly from GCC 4.9
For now regex is available in standard c++ with #include <regex> and using namespace std. I would want to add ^ and $ to check if the input string contains the whole number: ^-?[0-9]+([\.][0-9]+)?$
16

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

2 Comments

Note, this one also works with numbers like 2.2e4 and 5.3e-2. It is one of the best solutions to this question.
Could you explain a little more as to why this works? Don't really get the conditions
16

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]+"));
}

5 Comments

Ah, so it would. I've updated with a better solution. Thanks.
Shouldn't it be "[(-|+)|][0-9]+" (plus instead of star), otherwise your regex could match on "-" or "+" as a valid number.
Nice. I'm not sure what the (, | and ) are doing in that first character class--those metacharacters lose their special meaning inside a character class as far as I am aware. How about "^[-+]?[0-9]+$"?
It may be inefficient. Each time this is called, it calls std::regex constructor which compiles the regex.
This is slowest takes around 300us, averagely other takes 10us to 20us.
13

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:]]+)?))?" ) ) );
}

2 Comments

It seems that the line using namespace std; is unnecessary.
bool is_number(string& s) { const string reg = R"(-?[0-9]+([\.][0-9]+)?)"; return regex_match(s, regex(reg)); }
7

Try this:

isNumber(const std::string &str) {    
  return !str.empty() && str.find_first_not_of("0123456789") == string::npos;
}

1 Comment

This tests for unsigned integers only
5

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());
}

1 Comment

One disadvantage of this solution over some of the others, is there is no 'early exit'. i.e. If the first character is a letter, it will continue iterating through the entire string counting the rest of the characters.
5

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.

Comments

5

As it was revealed to me in an answer to my related question, I feel you should use boost::conversion::try_lexical_convert

Comments

4

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;
}

1 Comment

Thanks man! Scrolling all the way down was worthwhile!
4

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

Comments

4

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));
  }
}

Comments

4

Despite the many answers to this very old question, I think it is worth adding this one. With C++17 we have added the function from_chars. With it the whole can be treated also in such a way.

#include <string_view>
#include <iostream>
#include <charconv>

bool is_number(std::string_view s)
{
    double val;
    auto result = std::from_chars(s.data(),s.data()+s.size(),val);
    return result.ec != std::errc::invalid_argument;
}

int main()
{
    std:: cout << std::boolalpha;
    std::string s = "50";
    std::string s2 = "147.4";
    char cstr[5] = "1e-7";
    char invalid_cstr[6] = "five"; 
    std::cout << "1.456 is numberr: "  << is_number("1.456") << "\n";
    std::cout << s << " is numberr: " << is_number(s) << "\n";
    std::cout << s2 << " is numberr: " << is_number(s2) << "\n";
    std::cout << cstr << " is numberr: " << is_number(cstr) << "\n";
    std::cout << invalid_cstr << " is numberr: " << is_number(invalid_cstr) << "\n";
}

3 Comments

Vote up for the simple, yet effective solution.
This is a good solution. But this doesn't work if there is leading white space. For eg: " 42" will give false. Also gives false positives when there are trailing non-numeric characters. For eg: "15 foo" will give true. Both these examples are in from_chars docs
The simplest solution is to just use std::regex. Not necessarily the fastest wrt runtime, but certainly the fastest to write.
3

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.

1 Comment

I'd request downvoters to help me understand the issue, I'll improve my answer. Thanks.
3

We may use a stringstream class.

    bool isNumeric(string str)
    {
       stringstream stream;                   
       double number;

       stream<<str;
       stream>>number;

       return stream.eof();
    }

Comments

3

Using <regex>. This code was tested!

bool isNumber(const std::string &token)
{
    return std::regex_match(token, std::regex("(\\+|-)?[0-9]*(\\.?([0-9]+))$"));
}

Comments

3

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);
}

1 Comment

Short and acurate.
2

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();
}

2 Comments

this will return 10 for a string that contains the value "10_is_not_a_number".
"10_is_not_a_number" will put 10 into the variable d, but the return value will be false, since EOF is not reached.
2

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;
}

Comments

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())); 
}

2 Comments

If the number happens to be 0, you'll get a false-negative.
This will return any leading number, and not warn you of trailing garbage (e.g. "123hello" ==> 123). @Charles: Brendan mentions he only needs to recognise positive ints in a comment on another answer.
1

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
}

Comments

1
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.

1 Comment

While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.
1

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);
}

Comments

1
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();
}

2 Comments

cool, thank you for your answer. to make it even better, please add some text describing how it works.
It doesn't need comment, it is simple code, it iterates over the given string and checks if it contains only numbers. @Kirby
1
#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"
}

Comments

0

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).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.