tags:

views:

938

answers:

5

When using the function atoi (or strtol or similar functions for that matter), how can you tell if the integer conversion failed or if the c-string that was being converted was a 0 (zero)? For what I'm doing zero is an acceptable value and the c-string being converted may contain any number of zeros. It may also have leading whitespace.

Thanks!

+4  A: 

From the man page for strtol():

If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, however, strtol() stores the original value of nptr in *endptr. (Thus, if *nptr is not '\0' but **endptr is '\0' on return, the entire string was valid.)

gonzo
+6  A: 

Since this has the C++ tag:

template< typename T >
inline T convert(const std::string& str)
{
  std::istringstream iss(str);
  T obj;
  iss >> std::ws >> obj >> std::ws;
  if(!iss.eof()) throw "dammit!";
  return obj; 
}
sbi
I can use C++ so perhaps this is a better solution than using strtol, but I don't understand this code.
Jared
This is the best way to go.
Paul Nathan
@Jared: Is it easier to understand if you remove that `template< typename T>` and replace all `T` by `int`? The function initializes an input stream with the string to be read, creates an object, and tries to read from the stream into the object. If the reading fails or anything but whitespace is before/after what's to be read, the function should emit an error (which I've indicated by throwing an exception). If all went well, the object is returned. If you'd be a bit more specific on what's unclear, I could expand on that.
sbi
Now that I've taken some time to understand templates, this makes sense and is a perfect solution! Thanks! I found a more detailed post that has several different options too: http://stackoverflow.com/questions/1243428/convert-string-to-int-with-bool-fail-in-c
Jared
Yes, GMan's version there is a bit more sophisticated. `:)`
sbi
+1  A: 

It's been a while since I've done and C/C++, but it would appear to me that the (overly) simple solution would be to check just the string for "0".

int value = atoi(string_number.c_str());

if ( !value && string_number != "0" ) {
  // error
} else {
  // great success!
}
Justin Johnson
What about `00`, `+0`, `-0`, `<whitespace>0`. Are these errors too? I understand that they could be from the user's point of view. But keep in mind that these are not errors from traditional standard library point of view. For example, these are fine by `atoi`, `strtol`, `scanf`...
AndreyT
string_number may contain any number of zeros, so this is not an ideal solution, though I considered it. Thanks anyway.
Jared
+9  A: 

The proper function (as long as you are insisting on using C-style functions) is strtol and the conversion code might look as follows

const char *number = "10"; /* for example */

char *end;
long value = strtol(number, &end, 10); 
if (end == number || *end != '\0' || errno == ERANGE)
  /* ERROR, abort */;

/* Success */
/* Add whatever range checks you want to have on the value of `value` */

Some remarks:

strtol allows (meaning: quietly skips) whitespace in front of the actual number. If you consider this an error, you have to check for it yourself.

The check for *end != '\0' makes sure that there's nothing after the digits. If you want to permit other characters after the actual number (whitespace?), this check has to be modified accordingly.

P.S. I added the end == number check later to catch empty input sequences. "All whitespace" and "no number at all" inputs would have been caught by *end != '\0' check alone. It might make sense to catch empty input in advance though. In that case end == number check will/might become unnecessary.

AndreyT
It should be `end == number` (no dereference).
Chris Lutz
Oops... Sorry. Fixed. Thank you.
AndreyT
This is a good solution if confined to using C and not C++. I had only been exposed to `atoi` so I didn't know what options were available. Your use of `strol` is obviously a better C solution, but I am able to use C++ (only std libs though) so a template conversion function that simulates `lexical_cast` from the Boost library works best for my use. Thanks though! I'm sure this will come in handy sometime! :)
Jared
+1  A: 

An alternative to strtol is sscanf, although it's a little heavy-weight:

const char *numStr = "12345";  // input string
int value;
if(sscanf(numStr, "%d", &value) == 1)
    ;  // parsing succeeded, use value
else
    ;  // error

However, this allows leading whitespace in your string (which may or may not be desirable), and it allows anything to trail the number, so "123abc" would be accepted and return 123. If you want to have tighter control, go with strtol(), as AndreyT demonstrates.

Adam Rosenfield
`sscanf` is better that `atoi` since it provides at least some feedback. However, it is still limited. `sscanf`, unfortunately, can't survive overflow. In case of overflow, `sscanf` produces undefined behavior. This is why `strto...` family is basically the only reliable way to perform the conversion in C standard library.
AndreyT