41

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?

For what I'm doing, 0 is an acceptable value and the C-string being converted may contain any number of 0s. It may also have leading whitespace.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Jared
  • 1,254
  • 7
  • 19
  • 26
  • 2
    Why do you think strtol() is deprecated? It is a perfectly good, sound function from the C standard. If you care about success/failure, then you clearly should not use atoi(); there is no way for it to tell you whether it succeeded and returned 0 or failed and returned 0. – Jonathan Leffler Oct 28 '09 at 23:24
  • Sorry, I meant that since atoi is deprecated, if it won't work I'd be willing to use strtol if that's a good solution. – Jared Oct 28 '09 at 23:36
  • 3
    atoi is not deprecated it was never in the standard to begin with. It was an example from K&R that was seen as a useful extension. – stonemetal Oct 28 '09 at 23:49
  • 1
    Very well. I don't know the history or details of atoi and have removed any references to deprecation. – Jared Oct 28 '09 at 23:57
  • 5
    @stonemetal: Not true. `atoi` and the entire `ato...` company are standard functions. They are present in C99 as well. – AnT stands with Russia Oct 29 '09 at 00:11
  • @stonemetal I think you meant `itoa()`, which is MSVC++ only, last I heard. – Mateen Ulhaq Oct 30 '11 at 23:05
  • @muntoo you are right, though it must have been there at some point it has a man 3 entry http://linux.die.net/man/3/itoa – stonemetal Oct 31 '11 at 14:31
  • 1
    The accepted answer is outdated; see [emlai's answer](https://stackoverflow.com/a/28518981/4594973) instead. – code_dredd Aug 02 '17 at 06:00
  • @ray: Thanks for poking me. Been a long time since I've touched this question. – Jared Aug 04 '17 at 16:22
  • [Why shouldn't I use atoi()?](https://stackoverflow.com/q/17710018/995714) – phuclv Mar 04 '19 at 03:57

6 Answers6

51

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 what to treat such leading whitespace as 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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    It should be `end == number` (no dereference). – Chris Lutz Oct 29 '09 at 00:26
  • Oops... Sorry. Fixed. Thank you. – AnT stands with Russia Oct 29 '09 at 00:27
  • 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 Oct 29 '09 at 17:32
  • Would not the `(end == number)` check be sufficient? The C99 standard says (7.20.1.4-7 for the TR1,2 version of the standard) "If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a null pointer.". – mlvljr Jan 05 '11 at 15:42
  • @mlvljr: Good question. But what about overflow, for one example? Overflow situation does not fall under neither "empty sequence" nor "does not have the expected form" category. – AnT stands with Russia Jan 06 '11 at 01:56
  • @AndreyT Ah, really! I just didn't look what the standard exactly means by "the expected form". – mlvljr Jan 08 '11 at 09:30
  • `!= '\0'` is redundant. What is `errno == ERANGE` meant to do? – Alexander Oct 06 '14 at 21:51
  • @XAleXOwnZX: It is not redundant, as I explained in the answer. It is there to catch inputs like `123abc`. Such inputs are considered "correct" by `strtol` (it stops at `a` and produces `123`). The `!= '\0'` is there to catch such situations. – AnT stands with Russia Oct 06 '14 at 22:40
  • I didnt say `*end != '\0'`was redundant. `!=` is an operator that evaluates to 0 if both sides of it are the same. If *end is 0, then `*end != '\0'` evaluates to 0. If *end is nonzero, `*end != '\0'` evaluates to 1 (a non-zero it). Thus, all you need to write is `*end`. – Alexander Oct 06 '14 at 22:52
  • 1
    @XAleXOwnZX: What you are saying is formally correct. But I vocally object to using just `*end` on purely stylistic principles. In my book only values with explicit boolean semantics are allowed to be used in logical expressions without explicit comparison operator. All other values should be used with an explicit comparison operator, regardless of whether it is "redundant" or not. In this particular case I'd regard using `*end` in a logical expression as a bad coding style and a bad programming practice. I personally insist on using `*end != '\0'`. – AnT stands with Russia Oct 06 '14 at 23:57
  • Guys, the *title* has C in it. That takes precedence over the tags. – personal_cloud Sep 25 '17 at 23:36
  • I am confused, how does this tell the difference between "0" and "sdfdre". Based on the documentation `ERANGE` only happens when you are trying to convert a number out of range, which is not the same thing as trying convert a non number, all that does is return 0. – Jonathon Oct 23 '22 at 04:58
23

Since this is tagged :

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; 
}
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
sbi
  • 219,715
  • 46
  • 258
  • 445
  • I can use C++ so perhaps this is a better solution than using strtol, but I don't understand this code. – Jared Oct 28 '09 at 23:42
  • @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 Oct 29 '09 at 01:13
  • 3
    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 Oct 29 '09 at 17:35
  • Yes, GMan's version there is a bit more sophisticated. `:)` – sbi Oct 29 '09 at 18:57
  • This answer is outdated; see [emlai's answer](https://stackoverflow.com/a/28518981/4594973) instead. – code_dredd Aug 02 '17 at 05:59
  • @ray: I have upvoted that one. I can't change acceptance, though. – sbi Aug 02 '17 at 10:32
14

For C++11 and later:

The go-to function for string-to-integer conversion is now stoi, which takes a string and returns an int, or throws an exception on error.

No need for the verbose istringstream hack mentioned in the accepted answer anymore.

(There's also stol/stoll/stof/stod/stold for long/long long/float/double/long double conversions, respectively.)

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
6

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
2

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.

Community
  • 1
  • 1
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 6
    `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. – AnT stands with Russia Oct 29 '09 at 03:43
1

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
  • 30,978
  • 7
  • 65
  • 89
  • 4
    What about `00`, `+0`, `-0`, `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`... – AnT stands with Russia Oct 28 '09 at 23:36
  • 1
    string_number may contain any number of zeros, so this is not an ideal solution, though I considered it. Thanks anyway. – Jared Oct 28 '09 at 23:38
  • A year latter and a down vote? I wonder why. Something constructive, hopefully. – Justin Johnson Nov 06 '10 at 07:45