How to determine if a string is a number with C++?

2018-12-31 17:40发布

问题:

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

回答1:

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(), [](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.



回答2:

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


回答3:

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 }


回答4:

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



回答5:

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



回答6:

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.



回答7:

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)



回答8:

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


回答9:

I\'ve found the following code to be the most robust (c++11). It catches both integers and floats.

bool isNumber( std::string token )
{
    using namespace std;
    return std::regex_match( token, std::regex( ( \"((\\\\+|-)?[[:digit:]]+)(\\\\.(([[:digit:]]+)?))?\" ) ) );
}


回答10:

Try this:

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


回答11:

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


回答12:

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.



回答13:

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



回答14:

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


回答15:

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
}


回答16:

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\"

}



回答17:

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.



回答18:

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


回答19:

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



回答20:

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


回答21:

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



回答22:

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


回答23:

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.



回答24:

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


回答25:

We may use a stringstream class.

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

       stream<<str;
       stream>>number;

       return stream.eof();
    }


回答26:

Using <regex>. This code was tested!

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


回答27:

This function takes care of all the possible cases:

bool AppUtilities::checkStringIsNumber(std::string s){
    //Eliminate obvious irritants that could spoil the party
    //Handle special cases here, e.g. return true for \"+\", \"-\", \"\" if they are acceptable as numbers to you
    if (s == \"\" || s == \".\" || s == \"+\" || s == \"-\" || s == \"+.\" || s == \"-.\") return false;

    //Remove leading / trailing spaces **IF** they are acceptable to you
    while (s.size() > 0 && s[0] == \' \') s = s.substr(1, s.size() - 1);
    while (s.size() > 0 && s[s.size() - 1] == \' \') s = s.substr(0, s.size() - 1);


    //Remove any leading + or - sign
    if (s[0] == \'+\' || s[0] == \'-\')
        s = s.substr(1, s.size() - 1);

    //Remove decimal points
    long prevLength = s.size();

    size_t start_pos = 0;
    while((start_pos = s.find(\".\", start_pos)) != std::string::npos) 
        s.replace(start_pos, 1, \"\");

    //If the string had more than 2 decimal points, return false.
    if (prevLength > s.size() + 1) return false;

    //Check that you are left with numbers only!!
    //Courtesy selected answer by Charles Salvia above
    std::string::const_iterator it = s.begin();
    while (it != s.end() && std::isdigit(*it)) ++it;
    return !s.empty() && it == s.end();

    //Tada....
}


回答28:

Could you simply use sscanf\'s return code to determine if it\'s an int?

bool is_number(const std::string& s)
{
    int value;
    int result = sscanf(valueStr.c_str(), \"%d\", &value);
    return (result != EOF && readResult != 0);
}