26

I want to check if a string is a number with this code. I must check that all the chars in the string are integer, but the while returns always isDigit = 1. I don't know why that if doesn't work.

char tmp[16];
scanf("%s", tmp);

int isDigit = 0;
int j=0;
while(j<strlen(tmp) && isDigit == 0){
  if(tmp[j] > 57 && tmp[j] < 48)
    isDigit = 0;
  else
    isDigit = 1;
  j++;
}
Astinog
  • 1,151
  • 3
  • 12
  • 35
  • 8
    your range is wrong u want bigger than 57 and smaller than 48 .and it is impossible – qwr May 20 '13 at 07:52
  • The `scanf()` usage here is dangerous, as the input may overflow the `tmp` buffer. – jxh Jul 23 '15 at 00:25

17 Answers17

45

Forget about ASCII code checks, use isdigit or isnumber (see man isnumber). The first function checks whether the character is 0–9, the second one also accepts various other number characters depending on the current locale.

There may even be better functions to do the check – the important lesson is that this is a bit more complex than it looks, because the precise definition of a “number string” depends on the particular locale and the string encoding.

zoul
  • 102,279
  • 44
  • 260
  • 354
  • +1. They might also want to consider `-` and `.` (depending on locale, of course) – Moo-Juice May 20 '13 at 07:54
  • 5
    `isdigit` and `isnumber` are very good functions. However, they are really bad if you want to learn exactly how to distinguish numbers from other characters. To learn the basics of a function, or more precie, how a function works, is more important than to learn how to use a function. To me this answer is correct but in no way helps the OP in the long run. He still doesn't know how to distinguish numbers in other languages that lack `isdigit` and `isnumber`. Just my 2 cents. – Sani Huttunen Jun 01 '13 at 00:49
  • isnumber() is not standard C – Valerio Apr 09 '23 at 14:11
12
  if(tmp[j] >= '0' && tmp[j] <= '9') // should do the trick
Arun
  • 2,087
  • 2
  • 20
  • 33
8

More obvious and simple, thread safe example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
    if (argc < 2){
        printf ("Dont' forget to pass arguments!\n");
        return(-1);
    }

    printf ("You have executed the program : %s\n", argv[0]);

    for(int i = 1; i < argc; i++){
        if(strcmp(argv[i],"--some_definite_parameter") == 0){
            printf("You have passed some definite parameter as an argument. And it is \"%s\".\n",argv[i]);
        }
        else if(strspn(argv[i], "0123456789") == strlen(argv[i])) {
            size_t big_digit = 0;
            sscanf(argv[i], "%zu%*c",&big_digit);
            printf("Your %d'nd argument contains only digits, and it is a number \"%zu\".\n",i,big_digit);
        }
        else if(strspn(argv[i], "0123456789abcdefghijklmnopqrstuvwxyz./") == strlen(argv[i]))
        {
            printf("%s - this string might contain digits, small letters and path symbols. It could be used for passing a file name or a path, for example.\n",argv[i]);
        }
        else if(strspn(argv[i], "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == strlen(argv[i]))
        {
            printf("The string \"%s\" contains only capital letters.\n",argv[i]);
        }
    }
}
Gerhardh
  • 11,688
  • 4
  • 17
  • 39
Dennis V
  • 574
  • 7
  • 19
3

In this part of your code:

if(tmp[j] > 57 && tmp[j] < 48)
  isDigit = 0;
else
  isDigit = 1;

Your if condition will always be false, resulting in isDigit always being set to 1. You are probably wanting:

if(tmp[j] > '9' || tmp[j] < '0')
  isDigit = 0;
else
  isDigit = 1;

But. this can be simplified to:

isDigit = isdigit(tmp[j]);

However, the logic of your loop seems kind of misguided:

int isDigit = 0;
int j=0;
while(j<strlen(tmp) && isDigit == 0){
  isDigit = isdigit(tmp[j]);
  j++;
}

As tmp is not a constant, it is uncertain whether the compiler will optimize the length calculation out of each iteration.

As @andlrc suggests in a comment, you can instead just check for digits, since the terminating NUL will fail the check anyway.

while (isdigit(tmp[j])) ++j;
jxh
  • 69,070
  • 8
  • 110
  • 193
3
if ( strlen(str) == strlen( itoa(atoi(str)) ) ) {
    //its an integer
}

As atoi converts string to number skipping letters other than digits, if there was no other than digits its string length has to be the same as the original. This solution is better than innumber() if the check is for integer.

harvin
  • 76
  • 5
  • Hi! Just came across your anwear, I'm learning C, so this might be dumb question, but I have a program where I malloc data into an array (`char *array[4]; array[0] = (char*)malloc(10);`). Then I read value into this allocated array position (`scanf("%s", array[0]);`) and then I try to use your code like this: (`strlen(array[0]) == strlen( itoa(atoi(array[0])) )`), however I get the following error: `error: too few arguments to function 'itoa'`. As I said before, I'm new to this stuff, so this might be obvious, but I have no idea how to fix it :/. Could you please exmplain this to me? – TDiblik Mar 06 '22 at 09:56
  • 1
    Alternatively, you can just use `strcmp` instead. – Mehdi Charife Mar 26 '23 at 03:45
  • itoa() is not standard C, you can use printf() instead – Valerio Apr 09 '23 at 14:18
3
#include <stdio.h>
#include <string.h>
char isNumber(char *text)
{
    int j;
    j = strlen(text);
    while(j--)
    {
        if(text[j] > 47 && text[j] < 58)
            continue;

        return 0;
    }
    return 1;
}
int main(){
    char tmp[16];
    scanf("%s", tmp);

    if(isNumber(tmp))
        return printf("is a number\n");

    return printf("is not a number\n");
}

You can also check its stringfied value, which could also work with non Ascii

char isNumber(char *text)
{
    int j;
    j = strlen(text);
    while(j--)
    {
        if(text[j] >= '0' && text[j] <= '9')
            continue;

        return 0;
    }
    return 1;
}
Marcelo
  • 73
  • 1
  • 6
2

I need to do the same thing for a project I am currently working on. Here is how I solved things:

/* Prompt user for input */
printf("Enter a number: ");

/* Read user input */
char input[255]; //Of course, you can choose a different input size
fgets(input, sizeof(input), stdin);

/* Strip trailing newline */
size_t ln = strlen(input) - 1;
if( input[ln] == '\n' ) input[ln] = '\0';

/* Ensure that input is a number */
for( size_t i = 0; i < ln; i++){
    if( !isdigit(input[i]) ){
        fprintf(stderr, "%c is not a number. Try again.\n", input[i]);
        getInput(); //Assuming this is the name of the function you are using
        return;
    }
}
gsamaras
  • 71,951
  • 46
  • 188
  • 305
Mitchell Griest
  • 467
  • 1
  • 7
  • 21
1

None of these deal appropriately with negative numbers or floating point numbers.

How about:

bool
is_realnumber(char *instring) {
  if (*instring != '-' && *instring != '.' && !isdigit(*instring)) return false;
  if (strspn(instring+1, "0123456789.") < strlen(instring+1)) return false;
  int c = 0;
  while (*instring) if (*instring++ == '.') if (++c > 1) return false;
  return true;
}
ouflak
  • 2,458
  • 10
  • 44
  • 49
1

I can extend Marcelo's answer supporting floating numbers also as following:

char isnumber(const char *str)
{
     int decpos = -1, pmpos = -1, engpos = strlen(str) - 1, epmpos = strlen(str) - 1;
  for (int i = 0; i < strlen(str); i++)
    /* check if it is integer */
    if (str[i] > 47 && str[i] < 58)
      continue;
    /* check if it is decimal seperator and used once*/
    else if (str[i] == 46 && decpos == -1)
    {
      decpos = i;
      continue;
    }
    /* check if it is +/-, at the begining*/
    else if ((str[i] == 43 || str[i] == 45) && i == 0)
    {
      pmpos = 1;
      continue;
    }
    /* check if it is engineering format e/E, used once, after decimal and before +/-*/
    else if ((str[i] == 69 || str[i] == 101) && engpos == strlen(str) - 1 && i > 0 && i > decpos && i < epmpos)
    {
      engpos = 1;
      continue;
    }
    /* check if it is engineering format +/-, used once, after decimal and after engineering e/E*/
    else if ((str[i] == 43 || str[i] == 45) && epmpos == strlen(str) - 1 && i > 0 && i > decpos && i > engpos)
    {
      epmpos = 1;
      continue;
    }
    else
      return 0;
  return 1;
}
1

You can implement the function as following:

bool isNumeric(const char* s){
    while(*s){
        if(*s < '0' || *s > '9')
            return false;
        ++s;
    }
    return true;
}
1

If you intend to use that number in integer/long form in the future, you will be calling atoi or atol anyway, in which case you might want to just check the return value.

char tmp[16];
scanf("%s", tmp); // unsafe!!!
long tmp_long = atol(&tmp);
if (tmp_long == 0){
    printf("%s: is not a number.\n", &tmp);
}

tutorialspoint on atol

0

Your condition says if X is greater than 57 AND smaller than 48. X cannot be both greater than 57 and smaller than 48 at the same time.

if(tmp[j] > 57 && tmp[j] < 48)

It should be if X is greater than 57 OR smaller than 48:

if(tmp[j] > 57 || tmp[j] < 48)
Sani Huttunen
  • 23,620
  • 6
  • 72
  • 79
0

rewrite the whole function as below:

bool IsValidNumber(char * string)
{
   for(int i = 0; i < strlen( string ); i ++)
   {
      //ASCII value of 0 = 48, 9 = 57. So if value is outside of numeric range then fail
      //Checking for negative sign "-" could be added: ASCII value 45.
      if (string[i] < 48 || string[i] > 57)
         return FALSE;
   }

   return TRUE;
}
LinconFive
  • 1,718
  • 1
  • 19
  • 24
  • 2
    You should never use magic numbers in code. Especially if they are completely unnecessary. Instead of `48` use `'0'` etc.. Also calling `strlen` in each iteration of the loop is causing extra execution time. You should move that call before the loop. – Gerhardh Sep 27 '20 at 09:00
0

The problem is that the result of your code "isDigit" is reflecting only the last digit test. As far as I understand your qustion, you want to return isDigit = 0 whenever you have any character that is not a number in your string. Following your logic, you should code it like this:

char tmp[16];
scanf("%s", tmp);

int isDigit = 0;
int j=0;
isDigit = 1;  /* Initialised it here */
while(j<strlen(tmp) && isDigit == 0){
  if(tmp[j] > 57 || tmp[j] < 48) /* changed it to OR || */
    isDigit = 0;
  j++;
}

To get a more understandable code, I'd also change the test:

if(tmp[j] > 57 || tmp[j] < 48) 

to the following:

if(tmp[j] > '9' || tmp[j] < '0')
Fabio Vaz
  • 1
  • 2
0

Can check every char by isdigit().

#include <string.h>
#include <ctype.h>

int is_digit_str(char* str){
    int digit_count = 0;
    for (int i = 0; i < strlen(str); i++) {
      if(isdigit(str[i]))
         digit_count++;
      else
         break;
    }

    return digit_count == strlen(str);
}
david euler
  • 714
  • 8
  • 12
0

Hi you could use this method, it uses the isdigit() function that you get from including the ctype.h library.

This piece of code checks if the return value of isdigit() is zero (it means that the character that we are analyzing is a non digit) if so, it increments j.

The logic is using the j variable as an "opcode": if not equal to zero it has been incremented hence, the function encountered a non digit char, otherwise every single char that is present in the string is a digit.

# include <stdio.h>
# include <ctype.h>

int main(int argc, char *argv[])
{
  int i = 0;
  int j = 0;
  char data[] = "1234t6789";
  int check;

  while(data[i] != '\0'){
    if((check = isdigit(data[i])) == 0)
      j++;
    i++;
  }

  if(j != 0)
    printf("The string doesn't contain just numbers.\n");
  else printf("The string contains only numbers.\n");
  
  return 0;
}
Pit
  • 11
  • 3
-1
int being_number(char string[]){
int l=strlen(string);
for (int i = 0; i < l; i++)
{
    if(string[i] >= '0' && string[i] <= '9'){
        i=i; 
    }
    else{
        return(0);
    }
}
return(1);    

}