63

Or from the other way around find first non digit character.

Do the same functions apply for string and for char* ?

rsk82
  • 28,217
  • 50
  • 150
  • 240

8 Answers8

138

Of course, there are many ways to test a string for only numeric characters. Two possible methods are:

bool is_digits(const std::string &str)
{
    return str.find_first_not_of("0123456789") == std::string::npos;
}

or

bool is_digits(const std::string &str)
{
    return std::all_of(str.begin(), str.end(), ::isdigit); // C++11
}
Blastfurnace
  • 18,411
  • 56
  • 55
  • 70
  • 8
    Why here is double colon in isdigit ? It does not compile without it, `std::isdigit` doesn't work either. – Dfr Nov 09 '13 at 07:39
  • 5
    @Dfr: There are more than one `isdigit` functions (from `` and `` headers). [See this related answer.](http://stackoverflow.com/a/13262555/445976) – Blastfurnace Nov 09 '13 at 07:50
  • 2
    The c++11 way honestly rocks :) – dk123 May 05 '14 at 12:54
  • This method does not work if the wanted type is a floating number such as scientific format, sscanf is a better option. – Jichao Jun 12 '14 at 20:59
  • 1
    @Jichao: You're correct but that would be a different question. – Blastfurnace Jun 12 '14 at 21:05
  • @Blastfurnace : just out of curiosity I want to know why have you used pass by reference in the function above? – Amit Upadhyay Jan 18 '17 at 05:23
  • 4
    @AmitUpadhyay: I used references to avoid copying the passed `std::string` parameters (copying would be potentially expensive). They are also `const` to ensure I don't modify the caller's variables. Using const references this way is very common in C++ programming. – Blastfurnace Jan 18 '17 at 14:35
  • This is one old answer. But one should be aware that both of these return `true` for empty strings. As one is already looking for `0`, it would make sense to add `str.size() > 0` to the comparison. – Oliver Feb 16 '21 at 14:38
  • 1
    @Oliver but the question is about containing only digits or finding the first non-digit - and empty string does not contain any character that is not allowed. For checking that the string is not empty using `!str.empty()` is a bit more clear. – ABaumstumpf Nov 17 '22 at 14:49
16

Several people already mentioned to use isdigit(). However, note that this isn't entirely trivial because char can be signed which would cause a negative value to be passed to isdigit(). However, this function can only take positive values. That is, you want something akin to this:

if (s.end() == std::find_if(s.begin(), s.end(),
    [](unsigned char c)->bool { return !isdigit(c); })) {
    std::cout << "string '" << s << "' contains only digits\n";
}

It seems the reasoning for the conversion to unsigned char isn't obvious. So, here are the relevant quotes from their respective standards:

According to ISO/IEC 9899:2011 (or ISO/IEC 9899:1999) 7.4 paragraph 1 the following applies to the arguments of the functions from <ctype.h>:

... In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.

Unfortunately, the C++ standard doesn't specify that char is an unsigned type. Instead it specifies in ISO/IEC 14882:2011 3.9.1 [basic.fundamental] paragraph 1:

... It is implementation-defined whether a char object can hold negative values. ...

Clearly, a negative value cannot be represented as an unsigned char. That is, if char is using a signed type on an implementation (there are actually several which do, e.g., it is signed on MacOS using gcc or clang) there is the danger that calling any of the <ctype.h> function would cause undefined behavior.

Now, why does the conversion to unsigned char does the right things?

According to 4.7 [conv.integral] paragraph 2:

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note ]

That is, the conversion from a [potentially] signed char to unsigned char is well-defined and causes the result to be in the permitted range for the <ctype.h> functions.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I am not sure I understand you: Do you say that converting `char` to `unsigned char`, potentially producing overflows or UB, is a better idea than on relying on `isdigit()` checking for `c` being of `['0'..'9']`, which is specifically taking `int` as input? Any citations or justifications might be helpful, if possible. – Sebastian Mach Feb 13 '13 at 16:56
  • 2
    The conversion from `char` to `unsigned char` will not overflow or anything. It will retain the original bits but yield a value in the defined range of `isdigit()` even if `char` is signed and the character is in the negative range. The relevant quote is in C's 7.4 paragraph 1. – Dietmar Kühl Feb 13 '13 at 20:29
5

isdigit(int) tells you if a character is a digit. If you are going to assume ASCII and base 10, you can also use:

int first_non_digit_offset= strspn(string, "0123456789")
MSN
  • 53,214
  • 7
  • 75
  • 105
5

In the same spirit as Misha's answer, but more correct: sscanf(buf, "%*u%*c")==1.

scanf returns 0 if the %d digit extraction fails, and 2 if there is anything after the digits captured by %c. And since * prevents the value from being stored, you can't even get an overflow.

MSalters
  • 173,980
  • 10
  • 155
  • 350
4

The cctype header file has a good number of character classifications functions which you can use on each character in the string. For numeric checks, that would be isdigit.

The following program shows how to check each character of a C or C++ string ( the process is pretty much identical in terms of checking the actual characters, the only real difference being how to get the length):

#include <iostream>
#include <cstring>
#include <cctype>
int main (void) {
    const char *xyzzy = "42x";
    std::cout << xyzzy << '\n';
    for (int i = 0; i < std::strlen (xyzzy); i++) {
        if (! std::isdigit (xyzzy[i])) {
            std::cout << xyzzy[i] << " is not numeric.\n";
        }
    }

    std::string plugh ("3141y59");
    std::cout << plugh << '\n';
    for (int i = 0; i < plugh.length(); i++) {
        if (! std::isdigit (plugh[i])) {
            std::cout << plugh[i] << " is not numeric.\n";
        }
    }

    return 0;
}
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

#include <regex>

std::string string( "I only have 3 dollars!" );
std::cout << std::regex_search( string, std::regex( "\\d+" ) ); // true

and

std::string string( "I only have three dollars!" );
std::cout << std::regex_search( string, std::regex( "\\d+" ) ); // false
Shakiba Moshiri
  • 21,040
  • 2
  • 34
  • 44
0

From the cplusplus.com you can use isdigit function as follow:

// isdigit example (C++)
#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::isdigit
#include <sstream>        // std::stringstream

int main ()
{
  std::locale loc;
  std::string str="1776ad";
  if (isdigit(str[0],loc))
  {
    int year;
    std::stringstream(str) >> year;
    std::cout << "The year that followed " << year << " was " << (year+1) << ".\n";
  }
  return 0;
}

Note: there is 2 types of isdigit the other version is local independent and ASCII based.

sh.e.salh
  • 508
  • 2
  • 5
  • 16
-1

If it's a strict requirement that you can find exactly where the first non-character digit is, then you'll have to check each character. If not, I'd use either something like this:

unsigned safe_atoi(const std::string& a)
{
    std::stringstream s(a);
    unsigned b;
    s >> b;
    return b;
}
Yuushi
  • 25,132
  • 7
  • 63
  • 81
  • https://stackoverflow.com/questions/7677158/how-to-detect-negative-numbers-as-parsing-errors-when-reading-unsigned-integers – malat Feb 25 '22 at 10:28