[c] How to check if a string is a number?

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

This question is related to c string

The answer is


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

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.


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

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

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

  if(tmp[j] >= '0' && tmp[j] <= '9') // should do the trick

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)

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

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;