0

I have a helper function in my program that is

long parse_int (char * cp, int * i)
{
/* 
    Helper function

    cp: Pointer to a character array that is a base-10 string representation of an integer
     i: Pointer to an integer which will store the output of the parsing of cp

    Returns the number of characters parsed.
*/

    long n = 0;
    *i = 0;
    while (cp!= '\0')
    {
        char c = *cp;
        long k = index_of(digits, c);
        if (k > -1)
        {
            n = n * 10 + k;
        }
        else
        {
            break;
        }
        ++cp;
    }
    return n;
}

where the stuff used inside it is

long index_of (char * pc, char c)
{
/*
   Helper function

   pc: Pointer to the first character of a character array
    c: Character to search

   Returns the index of the first instance of
   character c in the character array str. If
   none is found, returns -1.
*/
    char * pfoc = strchr(pc, c); /* Pointer to the first occurrence of character c */
    return pfoc ? (pfoc - pc) : -1;
}

and

char digits [] = "0123456789";

If possible, I'd like to cut down on the amount of code by maximizing the use of standard library firepower. I'm fully aware of atoi, but the problem is that I can't invoke it and recover the number of characters it parsed when it was invoked. Any suggestions?

Leandro Papasidero
  • 3,728
  • 1
  • 18
  • 33
Steve Jobs
  • 181
  • 5

3 Answers3

0

You likely want to use strtol instead of atoi. The function signature for strtol has an outparameter to indicate the first invalid (non-digit) character)

 long int strtol(const char *nptr, char **endptr, int base);

So then you can just say this:

const char* pszNumber = "8942468XYZ";
char* pszEnd = NULL;
long val = strtol(pszNumber, &pszEnd, 10);

// pszEnd now points to "XYZ"
int number_of_characters_parsed = (int)(pszEnd - pszNumber);
selbie
  • 100,020
  • 15
  • 103
  • 173
  • How does that pointer subtraction work if pszEnd is NULL after running strtol? – Steve Jobs Feb 23 '15 at 05:21
  • As per: http://linux.die.net/man/3/strtol : `If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0). In particular, if *nptr is not '\0' but **endptr is '\0' on return, the entire string is valid.` – selbie Feb 23 '15 at 07:24
  • 1
    In other words, I don't think it's expected that strtol will ever set endptr to NULL. Are you observing something different? Perhaps endptr will be returned as NULL if you pass in NULL. But if you pass in a string that has no invalid characters, then endptr will point to the null terminator in the string. So the the above `pszEnd-pszNumber` still works. – selbie Feb 23 '15 at 07:27
0

The strtoX family of functions are considered better than atoX for this very reason.

You can provide a pointer to a variable which will receive the address of the first character that "failed" in the conversion.

The number of characters used in the conversion is then the difference between this value and the string itself (first character). In the code below, that would be nextChar - str.

For example, this is one way to check that an entire string is a valid long:

char *str = "12345";   // use your own test data here.
char *nextChar;
long val = strtol (str, &nextChar, 10);
if ((nextChar == str) || (*nextChar != '\0')) {
    printf ("Invalid input '%s'\n", str);
    return -1;
}

The two failure conditions are:

  • The next char is the start of the string, no characters were used, needed because the next test actually allows an empty string.
  • The next char isn't end of string, hence not all characters were consumed.
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
0

You can use strtol(), but you have to do a bit of work to be reasonably safe:

#include <errno.h>
#include <stdlib.h>
#include <limits.h>

int strtoi(const char *data, char **endptr, int base)
{
    int old_errno = errno;
    errno = 0;
    long lval = strtol(data, endptr, base);
    if (lval > INT_MAX)
    {
        errno = ERANGE;
        lval = INT_MAX;
    }
    else if (lval < INT_MIN)
    {
        errno = ERANGE;
        lval = INT_MIN;
    }
    if (errno == 0)
        errno = old_errno;
    return (int)lval;
}

This is my strtoi() function which uses the standard strtol() to do the hard work. It is careful not to (permanently) set errno to 0 since none of the standard library functions do that. You use it like:

char *end;
int intval = strtoi(str, &end, 0);

Or you can be sloppy and just use:

char *end;
int intval = strtol(str, &end, 0);

The difference comes when the 'subject' string is larger than fits in an int but not larger than fits in a long; the strtoi() function clamps the returned value to INT_MAX or INT_MIN, but the sloppy technique typically just gives you the low-order 4 bytes of whatever value was stored in the long, which can be misleading if you have 64-bit long and 32-bit int values.

Note that the calling code should check for intval == INT_MAX or intval == INT_MIN and errno != 0 to detect overflows.

You can write your parse_int() function in terms of this:

long parse_int (char *cp, int *i)
{
    char *end;
    *i = strtoi(cp, &end, 10);  // You want decimal
    return(end - cp);
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278