how to check if given c++ string or char* contains only digits?
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
}
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 anunsigned char
or shall equal the value of the macroEOF
. 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.
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")
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.
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;
}