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

40

185

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;
}
Shifra answered 11/1, 2011 at 5:51 Comment(9)
I hate seeing if (expr) return true; return false;! Just write return expr;.Aldis
@Aldis My style is to do the same as you. But is it really a big deal?Twitch
Your function prototype seems not appropriate. Why not use bool isParam(const string& line)Cymbre
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).Shifra
@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.Vannessavanni
@Vannessavanni 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.Wirework
if you want to know why his code is not working, check Brendan Weinstein and Tony Delroy answer below.Yugoslavia
This is definitely not a big deal. There is no complexity introduced. People who think more functionally prefer the immediate return of the bool, people who think more iteratively prefer the variant posted by the OP (and understand it quicker for that matter). Please stop forcing your sense of code beauty on others. This is nothing but personal preference.Rhines
@Aldis Isn't this something really dangerous, called a stacktrace?Cheyennecheyne
A
194

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.

Abreact answered 11/1, 2011 at 6:8 Comment(15)
Also doesn't handle negative numbers and non-whole numbers. We can't know what the requirements are based on the question.Twitch
Yeah. Sorry I should have been more specific. I know the numbers I am looking for are positive. This solution works very well.Shifra
You could also use !s.empty() && s.find_first_not_of("0123456789") == std::string::npos; for a C++03 one-liner.Historian
Also doesn't handle decimal numbers eg: 1.23Mar
To handle signed integers, after the std::string::const_iterator it = s.begin(); line you can add if(s[0] == '-' || s[0] == '+') it++;.Just being sure not empty input coming.Raki
Doesn't handle decimal numbers and scientific notation numbers!!Paraffin
And doesn't handle large numbers that do not fit in int.Sacrifice
@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.Abreact
@CharlesSalvia: The point I was trying to make is if a string is longer than 9 digits, it will likely not convert to int should the code need to do that conversion after validating the string. So the string length should be validated to make sure that the actual string contents are suitable for whatever purpose the string is going to be used for. Just checking for non-digits is not good enough if the string length is also not suitable.Sacrifice
This solution seems to be ugly and inflexible for me. Using regular expression is the most straightforward solution for a parsing task. This solution is not extensible (try writing matching a floating point number in the same style), does not check the sign, has logical flaws that have been found above, is much more complicated than a regexp.Barnie
@FredrickGauss There's a problem with this approach: Even "-" or "+" is considered a number then. There is an additional check required instead of !string.empty() in the end i.e. static bool isIntegerNumber(const std::string& string){ std::string::const_iterator it = string.begin(); int minSize = 0; if(string.size()>0 && (string[0] == '-' || string[0] == '+')){ it++; minSize++; } while (it != string.end() && std::isdigit(*it)) ++it; return string.size()>minSize && it == string.end(); }Dolorous
Thanks for noticing @Constantin. Edit for my suggestion: To handle signed integers, after the std::string::const_iterator it = s.begin(); line you can add if(s.size() > 1 && (s[0] == '-' || s[0] == '+')) it++;Raki
Don't forget to include <string> <algorithm> and <cctype> to make the C++11 example work.Jason
@Jason you're a hero! I really wish people got into the habit of mentioning which libraries they use in their anwersZeal
A good answer should work for all numbers like 3.2+e15, 3.2-e27, 3.2+E45, 44.9L, etc.Palette
H
125

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;
}
Harrietharriett answered 15/5, 2013 at 21:10 Comment(22)
This function discards white space in front. You thus have to check first char for isdigit.Scherman
@chmike: Based on my understanding of the question, discarding leading whitespace is the correct behavior (atoi as used in the question does this also).Harrietharriett
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.Scherman
@BenVoigt You're saying that p will be set to nullptr if strtol is sucessful, right? That's not what I'm seeing :(Pipes
@JonathanMee: No, p will be pointing to the NUL that terminates the string. So p != 0 and *p == 0.Harrietharriett
The above code doesn't handle decimal places, and strtod thinks that "NaN" is a number.Giuseppinagiustina
@Steve: strtol doesn't handle decimal places, because it is designed not to. long is an integral type. strtod doesn't think "NaN" is a number, it thinks it is a valid double.... and it is correct about that. If you want to test that something is a finite double, it is trivial to combine strtod with isfinite. There doesn't need to be a separate function for every possibility "finite double", "strictly positive double", "non-negative double", etc.Harrietharriett
The problem of strtol is, if the string is "0" how can you be sure ?Torse
@Jackt: As already explained, to see if parsing was successful check if *p == 0 not the return value.Harrietharriett
what if line contains trailing whitespace?Aurum
@user1282931: Then *p == 0 || iswhite(*p). But that would be a different behavior than the one wanted in the question, which indicated atoi.Harrietharriett
@user1282931: What did the documentation for strtol say when you read it?Harrietharriett
@Ben Voigt that's the thing, it doesn't say what happens to p if the conversion fails. As such iswhite(*p) might be true if the conversion failedAurum
@user1282931: p is left pointing where the conversion stopped (specifically, to the first character which couldn't be converted). Unless the conversion never started at all (because no digits were found), then p equals the input pointer. The official specification has a roundabout description of this involving a "final string", but the Linux man page gives a pretty straightforward description of the same rule.Harrietharriett
cppreference also describes how p is determined pretty clearly: "The functions sets the pointer pointed to by str_end to point to the character past the last character interpreted. If str_end is NULL, it is ignored. If the str is empty or does not have the expected form, no conversion is performed, and (if str_end is not NULL) the value of str is stored in the object pointed to by str_end."Harrietharriett
Cool solution but the condition wouldn't work if we had a space after the number (' ' != '\0'). IMO comparing s with p as stated in the docs would've made more sense.Samuelsamuela
@Hakim: If you want to accept, in addition to numbers, numbers with trailing stuff, then it is easy to adjust the test (use *p == 0 || iswhite(*p)). Comparing s to p wouldn't give you any control over what type of trailing stuff you accept. In general though "4 and twenty blackbirds" is not "a string that is a number" and my code correctly rejects it. It is only a string that starts with a number.Harrietharriett
I think the check must be return *s != 0 && *p == 0; to exclude empty strings. I would even precede that with assert(s); for completeness.Boarer
@Peter-ReinstateMonica: If *s == 0 you might as well not even bother calling strtol, so I disagree with the single combined test. But if your input could be an empty string then testing for that is needed.Harrietharriett
@BenVoigt Sure, one could handle that case on the calling side, but the general rule for "is this string a number" includes that test.Boarer
@Peter-ReinstateMonica: I mean it should be the first statement in isParam (early return without calling strtol), not that it needs to move to the caller.Harrietharriett
@BenVoigt Yes, true. Exclude the pathological cases before doing the main path.Boarer
D
58

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

Diamagnetism answered 31/7, 2013 at 16:38 Comment(2)
If there are utf8 characters in the string, you would get run time error.Fastigium
@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/isdigitFrye
B
33

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 }
Borrero answered 11/1, 2011 at 6:6 Comment(15)
Using try{} catch{} a good idea? Should we not avoid it as much as possible?Sarmentum
@Nawaz: No. There's a reason it's part of the C++ language. Like all C++ constructs, it should be used in the appropriate places. Given that the file read operation is I/O bound anyway, this doesn't seem excessive.Vannessavanni
Nawaz - there's no way to avoid it here. If you'd like to avoid it then you need to rewrite lexical_cast. As it is, lexical_cast is behaving correctly. If the value in the string is not a number then it can't do its job. This IS an exceptional situation for that function.Borrero
-1 for abusing try catch... blogs.msdn.com/b/ericlippert/archive/2008/09/10/…Investment
try{} catch{} is appropriate here. However, catch(...) is just bad practice. In this case, use boost::bad_lexical_cast for your exception handler.Stearne
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.Unswear
It's an abuse of exceptions, on Windows this would cause OS-dependent actions, which is not acceptable for a simple number decoding task.Barnie
@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.Borrero
@Investment Your own link says that try catch SHOULD be used in this case. "Vexing exceptions are thrown in a completely non-exceptional circumstance, and therefore must be caught and handled all the time."Nestornestorian
@Ville-ValtteriTiittanen: That's the definition of a vexing exception, not guidance to use them. (Note the quote said "must", you paraphrased incorrectly as "should")Harrietharriett
@BenVoigt How do you understand this part about converting strings to numbers? "The classic example of a vexing exception is Int32.Parse, which throws if you give it a string that cannot be parsed as an integer. But the 99% use case for this method is transforming strings input by the user, which could be any old thing, and therefore it is in no way exceptional for the parse to fail. -- You have to catch vexing exceptions"Nestornestorian
@Ville-ValtteriTiittanen: Keep reading, pay attention to where it says "Try to never write a library yourself that throws a vexing exception." and " Avoid vexing exceptions whenever possible by calling the “Try” versions of those vexing methods that throw in non-exceptional circumstances. If you cannot avoid calling a vexing method, catch its vexing exceptions." Well, you can avoid calling the function that produces a vexing exception. boost::lexical_cast is the version with vexing exceptions (bad!), strtod is the version which reports expected failures without using exceptions (good!)Harrietharriett
Buried down in the answer list is boost::conversion::try_lexical_convert<> as shown in @Investment answer: https://mcmap.net/q/135451/-how-to-determine-if-a-string-is-a-number-with-c This seems to me to be more sensible way to use lexical_cast for those enticed by this answer but troubled by abuse of try/catchKhorma
How do boost::lexical_cast's exceptions differ from those thrown by std::stoi / std::stol? (The latter throw invalid_argument if a conversion can't be performed, or out_of_range if the converted value is too large to be represented by the target type).Ronna
@AlexanderGuyer - it has its own. This answer is ancient and useless. You should use the new library features given by std.Borrero
D
21

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.

Demonology answered 9/5, 2013 at 15:48 Comment(10)
error: 'strspn' was not declared in this scope I think this is because I am missing a "#include" but what oneDauntless
If you're going to use std::string, use its find_first_not_of member function.Harrietharriett
This would fail if you pass in a string of "12.3-4.55-", which is obviously not a valid numberChessa
Buzzrick, the proposed answer already states that this would fail with the non-number you mentioned.Demonology
if you limit it only to "0123456789" then the formula is perfect to test for unsigned integerDisfigure
This is not a valid answer. Your code fails for an input like: '1Z'Mweru
@Sampath, please explain how this is wrong. This code will return false if "1Z" is passed in. Is "1Z" a number?Demonology
@DavidRector: Apologies, I have made a mistake in the example. It should be corrected as: '1-'. Even strings like 1.1.1.1 or 1-2-3-321 would return true. The problem is that the strspn does not check the order nor restrict occurrence count, but simply count occurrences.Mweru
@Sampath, if you read my complete answer, that problem of decimal points and minus signs is already mentioned in what I wrote. Your comment does not provide any additional information that I didn't already supply. In fact, all I did was show how strspn() could be used instead of other iteration solutions that others already presented before me.Demonology
@DavidRector: I'm afraid you are correct. And your example of strspn is good.Mweru
M
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)

Marginalia answered 11/1, 2011 at 8:8 Comment(2)
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.Barnie
@EarlGray: Regex is only available properly from GCC 4.9Chariot
S
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

Suprasegmental answered 15/5, 2013 at 21:48 Comment(1)
Note, this one also works with numbers like 2.2e4 and 5.3e-2. It is one of the best solutions to this question.Huston
B
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]+"));
}
Beneath answered 1/2, 2014 at 20:36 Comment(5)
Ah, so it would. I've updated with a better solution. Thanks.Beneath
Shouldn't it be "[(-|+)|][0-9]+" (plus instead of star), otherwise your regex could match on "-" or "+" as a valid number.Jaundice
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]+$"?Lunula
It may be inefficient. Each time this is called, it calls std::regex constructor which compiles the regex.Tewell
This is slowest takes around 300us, averagely other takes 10us to 20us.Hibernaculum
E
12

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:]]+)?))?" ) ) );
}
Esmeraldaesmerelda answered 26/12, 2015 at 11:43 Comment(1)
It seems that the line using namespace std; is unnecessary.Viosterol
I
7

Try this:

isNumber(const std::string &str) {    
  return !str.empty() && str.find_first_not_of("0123456789") == string::npos;
}
Invective answered 7/6, 2016 at 11:29 Comment(1)
This tests for unsigned integers onlyDisfigure
H
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());
}
Harslet answered 15/5, 2013 at 19:56 Comment(1)
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.Galaxy
E
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.

Elliot answered 30/3, 2014 at 7:10 Comment(0)
I
5

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

Investment answered 14/3, 2017 at 21:25 Comment(0)
B
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;
}
Batsheva answered 11/1, 2011 at 5:51 Comment(1)
Thanks man! Scrolling all the way down was worthwhile!Jamarjamb
I
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

Insecurity answered 14/7, 2016 at 18:21 Comment(0)
A
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.

Appearance answered 5/2, 2017 at 14:51 Comment(1)
I'd request downvoters to help me understand the issue, I'll improve my answer. Thanks.Appearance
E
3

We may use a stringstream class.

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

       stream<<str;
       stream>>number;

       return stream.eof();
    }
Easterly answered 2/11, 2017 at 6:48 Comment(0)
B
3

Using <regex>. This code was tested!

bool isNumber(const std::string &token)
{
    return std::regex_match(token, std::regex("(\\+|-)?[0-9]*(\\.?([0-9]+))$"));
}
Beheld answered 30/11, 2017 at 6:55 Comment(0)
B
3

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";
}
Benelux answered 10/3, 2023 at 18:20 Comment(2)
Vote up for the simple, yet effective solution.Muniments
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 docsMinimalist
D
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();
}
Daedal answered 2/8, 2015 at 20:37 Comment(2)
this will return 10 for a string that contains the value "10_is_not_a_number".Fanning
"10_is_not_a_number" will put 10 into the variable d, but the return value will be false, since EOF is not reached.Clabber
W
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;
}
Wu answered 16/4, 2019 at 12:10 Comment(0)
S
2

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);
}
Shudder answered 28/5, 2019 at 8:44 Comment(0)
J
2

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));
  }
}
January answered 18/3, 2022 at 14:56 Comment(0)
S
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())); 
}
Shifra answered 11/1, 2011 at 6:36 Comment(2)
If the number happens to be 0, you'll get a false-negative.Abreact
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.Clamant
C
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
}
Cistercian answered 6/4, 2015 at 9:47 Comment(0)
P
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"
}
Posology answered 19/11, 2015 at 7:24 Comment(0)
F
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.

Fanning answered 22/11, 2015 at 3:52 Comment(1)
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.Priedieu
M
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);
}
Misdirection answered 1/3, 2017 at 13:16 Comment(0)
A
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();
}
Azotemia answered 22/12, 2020 at 7:10 Comment(2)
cool, thank you for your answer. to make it even better, please add some text describing how it works.Grooved
It doesn't need comment, it is simple code, it iterates over the given string and checks if it contains only numbers. @GroovedWesla
C
1

The simple and most basic way to determine if a string is a digit is by checking the first char of the string only. Then check it normally with isdigit().

string token = "99"
if(isdigit(token[0])) // CHECK FIRST CHAR OF THE STRING FOR NUMBER
        cout << token <<"\n";
Calcify answered 13/6, 2021 at 3:11 Comment(0)
F
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).

Fideism answered 15/8, 2014 at 6:49 Comment(0)
T
0

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...
}
Trucker answered 26/11, 2014 at 12:23 Comment(0)
T
0

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....
}
Terefah answered 3/4, 2018 at 19:24 Comment(0)
T
0

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);
}
Tartan answered 30/11, 2018 at 22:18 Comment(0)
J
0

To validate a string for a Number.

#include<bits/stdc++.h>
using namespace std;
int main()
{
    char input[100];
    gets(input);
    int l = strlen(input);
    int flag = 0;
    for(int i = 0; i<l; i++)
    {
        if(input[i] < '0' || input[i] > '9')
        {
            flag = 1;
            break;
        }
    }
    if(flag == 0)
    cout << "Input is a Number! " ;
    else
    cout << "Input is Not a Number! " ;
    return 0;
}
Jacobo answered 23/12, 2021 at 8:12 Comment(0)
O
0

You can use std::sto{f,i}() and try catch.

it's not good idea , but not bad to have function that determine this.

#include <iostream>
#include <string>

int main() {
  try {
    auto a1 = std::stoi("h024");
    //    auto a1 = std::stoi("24"); // ok
    std::cout << a1 << std::endl;
  } catch (std::exception const& err) {
    std::cout
        << "You did not enter valid input(number) , error in parsing input."
        << std::endl;
  }
  return 0;
}

Ref

Ophelia answered 21/12, 2022 at 7:11 Comment(0)
M
0

Checking a lot of answers that doesn't consider multiple "dots" and minus sign, here's a quick solution that solves them:

bool is_number(std::string_view s)
{
    bool numeric = false;
    if (s.size()) {
        bool first_char_valid = numeric = (isdigit(s.front()) || (s.front() == '-' && s.size() > 1));
        if (first_char_valid) {
            bool found_dot = false;
            for (auto it = s.begin(); it != s.end(); it++) {
                if (isdigit(*it)) continue;
                else if (*it=='.' && !found_dot) {
                    found_dot = true;
                    continue;
                }
                numeric = false;
                break;
            }
        }
    }
    return numeric;
}

Note that this doesn't resolve scientific symbols as "E", nor whitespaces.

I was originally delighted by @whoami's answer, but after making a benchmark I decided to add this answer, which shows an increased performance of x1300 from the version std::from_chars!

Checkout at: https://onlinegdb.com/GHFgHNnDd

Muniments answered 5/9, 2023 at 10:59 Comment(0)
A
-1

You may test if a string is convertible to integer by using boost::lexical_cast. If it throws bad_lexical_cast exception then string could not be converted, otherwise it can.

See example of such a test program below:

#include <boost/lexical_cast.hpp>
#include <iostream>

int main(int, char** argv)
{
        try
        {
                int x = boost::lexical_cast<int>(argv[1]);
                std::cout << x << " YES\n";
        }
        catch (boost::bad_lexical_cast const &)
        {
                std:: cout << "NO\n";
        }
        return 0;
}

Sample execution:

# ./a.out 12
12 YES
# ./a.out 12/3
NO
Anthelion answered 3/3, 2020 at 18:15 Comment(0)
P
-1

You can try this method.

bool isNumber(std::string line)
{
    char karakters[] = {'0','1','2','3','4','5','6','7','8','9','\0'};
    unsigned int oksize = 0;

    for (int i = 0; i < line.size(); i++) {
        char k = (char) line[i];
        for(int j = 0; j < sizeof(karakters) / sizeof(char); j++) {
            if(k == karakters[j]) {
                oksize += 1;
                break;
            }
        }

    }

    if (oksize == (line.size() - 1) && oksize > 0) {
        return true;
    } else {
        return false;
    }
}
Perforated answered 9/1 at 9:11 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Hofmann
H
-1
bool is_number(string number) {
   return !(number.size() == 1 and number[0] < 48);
}

works for everything.

Heald answered 30/1 at 7:8 Comment(2)
"works for everything" - Really? You want to go on record stating that? You may be correct for ASCII, but that's hardly "everything" - what about UTF8, UTF16, UTF32, EBCDIC and more?Crusted
@JesperJuhl yo, yo, what are those !!? ;-PHeald

© 2022 - 2024 — McMap. All rights reserved.