Rate this page

Flattr this

Safely parse an integer using the standard C library

Tested on

Debian (Etch, Lenny, Squeeze)
Fedora (14)
Ubuntu (Hardy, Intrepid, Jaunty, Karmic, Lucid, Maverick, Natty, Precise, Trusty)

Objective

To convert a string to an integer using the standard C library, giving an error for any input that is not a valid integer

Background

The standard library provides two sets of functions for parsing integers:

Scenario

Suppose that s is a pointer to the string to be parsed, which is null-terminated.

Methods

Method (signed integers)

The following code fragment reports as an error any input string that cannot be converted in full:

errno = 0;
char* end = s;
long int result = strtol(s,&end,10);
if (errno == ERANGE) {
  printf("Error: integer overflow\n");
} else if (*end) {
  printf("Error: not a valid integer\n");
} else {
  printf("Result is: %ld",result);
}

You can replace strtol with strtoll provided you make a corresponding change to the result type. The third argument is the radix, which is decimal in this example but can be changed to any required value in the range 2 to 36. A radix of zero defaults to decimal, but allows octal with a prefix of ‘0’ and hexadecimal with a prefix of ‘0x’.

Setting errno to zero is necessary because (unlike most standard library functions) strtol provides no other means to determine whether errno has been set. A return value of LONG_MIN or LONG_MAX is not a reliable indication because it could occur as the result of a valid input.

Method (unsigned integers)

The method for parsing unsigned integers is similar, however there is a complication. Negative values are not rejected by stdtoul: instead the result is negated, typically causing it to wrap around to a large positive value.

You can prevent this from going undetected by checking whether the first character of the input is a digit. Unfortunately this prevents the conversion function from skipping whitespace, so if you wish to retain that feature then you must implement it explicitly:

while (isspace(*s)) ++s;
errno = 0;
char* end = s;
long int result = strtol(s,&end,10);
if (errno == ERANGE) {
  printf("Error: integer overflow\n");
} else if ((!isdigit(*s)||*s=='+')||*end) {
  printf("Error: not a valid integer\n");
} else {
  printf("Result is: %ld",result);
}

Variations

Allowing partial conversion

If you are willing to accept a partial conversion then instead of checking that *end is null you should check whether end has advanced from its initial value of s:


if (errno == ERANGE) {
  printf("Error: integer overflow\n");
} else if (end==s) {
  printf("Error: not a valid integer\n");
} else {
  printf("Result is: %ld (converted %u characters)",result,end-s);
}

Tags: c | c++