I am trying to find out if there is an alternative way of converting string to integer in C.
I regularly pattern the following in my code.
char s[] = "45";
int num = atoi(s);
So, is there a better way or another way?
I am trying to find out if there is an alternative way of converting string to integer in C.
I regularly pattern the following in my code.
char s[] = "45";
int num = atoi(s);
So, is there a better way or another way?
There is strtol which is better IMO. Also I have taken a liking in strtonum, so use it if you have it (but remember it's not portable):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
You might also be interested in strtoumax and strtoimax which are standard functions in C99. For example you could say:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
Anyway, stay away from atoi:
The call atoi(str) shall be equivalent to:
(int) strtol(str, (char **)NULL, 10)except that the handling of errors may differ. If the value cannot be represented, the behavior is undefined.
strtonum? I keep getting an implicit declaration warning#<stdlib.h>. However, you could use the standard strtoumax alternative.Robust C89 strtol-based solution
With:
atoi family)strtol (e.g. no leading whitespace nor trailing trash chars)#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {
STR2INT_SUCCESS,
STR2INT_OVERFLOW,
STR2INT_UNDERFLOW,
STR2INT_INCONVERTIBLE
} str2int_errno;
/* Convert string s to int out.
*
* @param[out] out The converted int. Cannot be NULL.
*
* @param[in] s Input string to be converted.
*
* The format is the same as strtol,
* except that the following are inconvertible:
*
* - empty string
* - leading whitespace
* - any trailing characters that are not part of the number
*
* Cannot be NULL.
*
* @param[in] base Base to interpret string in. Same range as strtol (2 to 36).
*
* @return Indicates if the operation succeeded, or why it failed.
*/
str2int_errno str2int(int *out, char *s, int base) {
char *end;
if (s[0] == '\0' || isspace((unsigned char) s[0]))
return STR2INT_INCONVERTIBLE;
errno = 0;
long l = strtol(s, &end, base);
/* Both checks are needed because INT_MAX == LONG_MAX is possible. */
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
return STR2INT_OVERFLOW;
if (l < INT_MIN || (errno == ERANGE && l == LONG_MIN))
return STR2INT_UNDERFLOW;
if (*end != '\0')
return STR2INT_INCONVERTIBLE;
*out = l;
return STR2INT_SUCCESS;
}
int main(void) {
int i;
/* Lazy to calculate this size properly. */
char s[256];
/* Simple case. */
assert(str2int(&i, "11", 10) == STR2INT_SUCCESS);
assert(i == 11);
/* Negative number . */
assert(str2int(&i, "-11", 10) == STR2INT_SUCCESS);
assert(i == -11);
/* Different base. */
assert(str2int(&i, "11", 16) == STR2INT_SUCCESS);
assert(i == 17);
/* 0 */
assert(str2int(&i, "0", 10) == STR2INT_SUCCESS);
assert(i == 0);
/* INT_MAX. */
sprintf(s, "%d", INT_MAX);
assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
assert(i == INT_MAX);
/* INT_MIN. */
sprintf(s, "%d", INT_MIN);
assert(str2int(&i, s, 10) == STR2INT_SUCCESS);
assert(i == INT_MIN);
/* Leading and trailing space. */
assert(str2int(&i, " 1", 10) == STR2INT_INCONVERTIBLE);
assert(str2int(&i, "1 ", 10) == STR2INT_INCONVERTIBLE);
/* Trash characters. */
assert(str2int(&i, "a10", 10) == STR2INT_INCONVERTIBLE);
assert(str2int(&i, "10a", 10) == STR2INT_INCONVERTIBLE);
/* int overflow.
*
* `if` needed to avoid undefined behaviour
* on `INT_MAX + 1` if INT_MAX == LONG_MAX.
*/
if (INT_MAX < LONG_MAX) {
sprintf(s, "%ld", (long int)INT_MAX + 1L);
assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
}
/* int underflow */
if (LONG_MIN < INT_MIN) {
sprintf(s, "%ld", (long int)INT_MIN - 1L);
assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
}
/* long overflow */
sprintf(s, "%ld0", LONG_MAX);
assert(str2int(&i, s, 10) == STR2INT_OVERFLOW);
/* long underflow */
sprintf(s, "%ld0", LONG_MIN);
assert(str2int(&i, s, 10) == STR2INT_UNDERFLOW);
return EXIT_SUCCESS;
}
str2int(). Pedantic: use isspace((unsigned char) s[0]).(unsigned char) cast could make a difference?l > INT_MAX and l < INT_MIN are pointless integer comparison since either result is always false. What happens if I change them to l >= INT_MAX and l <= INT_MIN to clear the warnings? On ARM C, long and int are 32-bit signed Basic data types in ARM C and C++if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW; would be better as if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;} to allow calling code to use errno on int out-of-range. Same for if (l < INT_MIN....Don't use functions from ato... group. These are broken and virtually useless. A moderately better solution would be to use sscanf, although it is not perfect either.
To convert string to integer, functions from strto... group should be used. In your specific case it would be strtol function.
sscanf actually has undefined behavior if it tries to convert a number outside the range of its type (for example, sscanf("999999999999999999999", "%d", &n)).atoi provides no meaningful success/failure feedback and has undefined behavior on overflow. sscanf provides success/failure feedback of sorts (the return value, which is what makes it "moderately better"), but still has undefined behavior on overflow. Only strtol is a viable solution.sscanf. (Though I confess I sometimes use atoi, usually for programs that I don't expect to survive more than 10 minute before I delete the source.)You can code atoi() for fun:
int my_getnbr(char *str)
{
int result;
int puiss;
result = 0;
puiss = 1;
while (('-' == (*str)) || ((*str) == '+'))
{
if (*str == '-')
puiss = puiss * -1;
str++;
}
while ((*str >= '0') && (*str <= '9'))
{
result = (result * 10) + ((*str) - '0');
str++;
}
return (result * puiss);
}
You can also make it recursive, which can fold in 3 lines.
code ((*str) - '0') code"----1" 2) Has undefined behavior with int overflow when the result should be INT_MIN. Consider my_getnbr("-2147483648")while (('-' == (*str)) || ((*str) == '+')) and not while ( *str == '-' || *str == '+')? Also while ((*str >= '0') && (*str <= '9')) is like Pascal programmer with wrong habits. In C this is while (*str >= '0' && *str <= '9').int atoi(const char* str){
int num = 0;
int i = 0;
bool isNegetive = false;
if(str[i] == '-'){
isNegetive = true;
i++;
}
while (str[i] && (str[i] >= '0' && str[i] <= '9')){
num = num * 10 + (str[i] - '0');
i++;
}
if(isNegetive) num = -1 * num;
return num;
}
int overflow.As already mentioned, the atoi family of functions should never be used in any C program, since they don't have any error handling.
The the strtol family of functions is 100% equivalent, but with extended functionality: it has error handling and it also supports other bases than decimal, such as hex or binary. Therefore the correct answer is: use strtol (family).
If you for some reason insist on rolling out this function yourself manually, you should try to do something similar to strtol in case there are other symbols present other than the optional sign and digits. It's quite common that we want to convert numbers that are part of larger string, for example.
A naive version with error handling support might look like the example below. This code is for decimal base 10 numbers only, but otherwise behaves like strtol with an optional pointer set to point at the first invalid symbol encountered (if any). Also note that this code doesn't handle overflows.
#include <ctype.h>
long my_strtol (char* restrict src, char** endptr)
{
long result=0;
long sign=1;
if(endptr != NULL)
{
/* if input is ok and endptr is provided,
it will point at the beginning of the string */
*endptr = src;
}
if(*src=='-')
{
sign = -1;
src++;
}
for(; *src!='\0'; src++)
{
if(!isdigit(*src)) // error handling
{
if(endptr != NULL)
{
*endptr = src;
}
break;
}
result = result*10 + *src - '0';
}
return result * sign;
}
To handle overflows, one can for example add code counting the characters and check that they never go past 10, assuming 32 bit long which can be max 2147483647, 10 digits.
atoi.)my_strtol("-21474836478", ...) (overflow), even though INT_MIN should be a valid result with an overflow - various ways to repair. 2) does not fail "-". 3) Does not pass strings beginning with "+". Deserves re-work.Just wanted to share a solution for unsigned long aswell.
unsigned long ToUInt(char* str)
{
unsigned long mult = 1;
unsigned long re = 0;
int len = strlen(str);
for(int i = len -1 ; i >= 0 ; i--)
{
re = re + ((int)str[i] -48)*mult;
mult = mult*10;
}
return re;
}
const char *.48 mean? Are you assuming that's the value of '0' where the code will run? Please don't inflict such broad assumptions on the world!'0' like you should.So, is there a better way or another way?
Many answers over the years here, yet they lack usage, attention to overflow and/or undefined behavior (UB) details, so here is my take:
Use strtol()
Say you want to convert a string to an int.
#include <limits.h>
#include <stdlib.h>
// Function signature similar to the long library function
// long int strtol(const char * restrict nptr, char ** restrict endptr, int base);
int my_strtoi(const char * restrict nptr, char ** restrict endptr, int base) {
#if INT_MAX == LONG_MAX && INT_MIN == LONG_MIN
// Just call the long version
return (int) strtol(nptr, endptr, base);
#else
long lval = strtol(nptr, endptr, base);
if (lval > INT_MAX) {
lval = INT_MAX;
errno = ERANGE;
} else if (lval < INT_MIN) {
lval = INT_MIN;
errno = ERANGE;
}
return (int) lval;
#endif
}
To use this function, set errno and check results.
errno = 0;
char *endptr;
int val = my_strtoi(nptr, &endptr, base);
if (nptr == endptr) {
puts("No conversion");
} else if (errno) {
// Overflow or some other implementation specific error occurred.
printf("Error %d, val:%d\n", errno, val);
} else {
// We may want to look for trailing non-numeric text,
// Leave that for later.
// Otherwise
printf("Success, val:%d\n", val);
}
Yes, but how about without strtol()?
My attempt to perform like strtol(). Notice the below 50 lines do not even handle base != 10. Rolling your own is a good exercise, yet it is non-trivial.
Look for leading white-space.
Look for optional sign character
Parse for required digits.
Avoid overflow. (This part adds a fair amount of code.)
Below only handles base 10. I'll save a general base 2 to 36 one for later.
// Unchecked code - will test later.
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
int my_strtoi(const char *restrict nptr, char **restrict endptr) {
if (nptr == NULL) {
; // This test is not required to match strtol().
// Leave it as stub code for later.
}
// Use of `is...()` functions oblige access to characters as unsigned char.
const unsigned char *unptr = (const unsigned char*) nptr;
// Consume optional leading white-spaces.
while (isspace(*unptr)) {
unptr++;
}
// Parse for an optional sign character.
bool negsign = *unptr == '-';
if (negsign || *unptr == '+') {
unptr++;
}
int sum = 0;
bool digit_found = isdigit(*unptr);
// Parse the digits.
while (isdigit(*unptr)) {
int digit = *unptr++ - '0';
// Is math in range?
if ((sum > INT_MIN / 10)
|| ((sum == INT_MIN / 10) && (digit <= -(INT_MIN % 10)))) {
// Accumulate on the negative side as there are more of them than positive.
// Note the subtraction.
sum = sum * 10 - digit;
} else {
sum = INT_MIN;
errno = ERANGE;
}
}
// Was the sign not negative?
if (!negsign) {
// Was sum too large?
if (sum < -INT_MAX) {
sum = INT_MAX;
errno = ERANGE;
} else {
sum = -sum;
}
}
if (endptr) {
*endptr = digit_found ? (char*) unptr : (char*) nptr;
}
return sum;
}
Ok, I had the same problem.I came up with this solution.It worked for me the best.I did try atoi() but didn't work well for me.So here is my solution:
void splitInput(int arr[], int sizeArr, char num[])
{
for(int i = 0; i < sizeArr; i++)
// We are subtracting 48 because the numbers in ASCII starts at 48.
arr[i] = (int)num[i] - 48;
}
'0'.To parse an integer from a string i'm using this function in C99 :
long long int to_int(char *s) {
long long int res = 0;
long long int sign = *s == '-' ? (++s, -1) : (s += *s == '+', 1);
if (*s >= '1' && *s <= '9')
for (--s; *++s && *s >= '0' && *s <= '9' && !(res >> 32);)
res = res * 10 + (*s - '0');
return *s || res >> 32 ? 0 : res * sign;
}
It return the number (positive or negative), zero in case of error.
s as LLONG_MIN incurs overflow (UB), for (--s; is UB when string does not start with a sign character, res >> 32 is not relevant for a long long.You can always roll your own!
#include <stdio.h>
#include <string.h>
#include <math.h>
int my_atoi(const char* snum)
{
int idx, strIdx = 0, accum = 0, numIsNeg = 0;
const unsigned int NUMLEN = (int)strlen(snum);
/* Check if negative number and flag it. */
if(snum[0] == 0x2d)
numIsNeg = 1;
for(idx = NUMLEN - 1; idx >= 0; idx--)
{
/* Only process numbers from 0 through 9. */
if(snum[strIdx] >= 0x30 && snum[strIdx] <= 0x39)
accum += (snum[strIdx] - 0x30) * pow(10, idx);
strIdx++;
}
/* Check flag to see if originally passed -ve number and convert result if so. */
if(!numIsNeg)
return accum;
else
return accum * -1;
}
int main()
{
/* Tests... */
printf("Returned number is: %d\n", my_atoi("34574"));
printf("Returned number is: %d\n", my_atoi("-23"));
return 0;
}
This will do what you want without clutter.
strto... family of functions. They are portable and significantly better.0x2d, 0x30 instead of '-', '0'. Does not allow '+' sign. Why (int) cast in (int)strlen(snum)? UB if input is "". UB when result is INT_MIN due to int overflow with accum += (snum[strIdx] - 0x30) * pow(10, idx);This function will help you
int strtoint_n(char* str, int n)
{
int sign = 1;
int place = 1;
int ret = 0;
int i;
for (i = n-1; i >= 0; i--, place *= 10)
{
int c = str[i];
switch (c)
{
case '-':
if (i == 0) sign = -1;
else return -1;
break;
default:
if (c >= '0' && c <= '9') ret += (c - '0') * place;
else return -1;
}
}
return sign * ret;
}
int strtoint(char* str)
{
char* temp = str;
int n = 0;
while (*temp != '\0')
{
n++;
temp++;
}
return strtoint_n(str, n);
}
Ref: http://amscata.blogspot.com/2013/09/strnumstr-version-2.html
atoi and friends is that if there's overflow, it's undefined behavior. Your function does not check for this. strtol and friends do.//I think this way we could go :
int my_atoi(const char* snum)
{
int nInt(0);
int index(0);
while(snum[index])
{
if(!nInt)
nInt= ( (int) snum[index]) - 48;
else
{
nInt = (nInt *= 10) + ((int) snum[index] - 48);
}
index++;
}
return(nInt);
}
int main()
{
printf("Returned number is: %d\n", my_atoi("676987"));
return 0;
}
In C++, you can use a such function:
template <typename T>
T to(const std::string & s)
{
std::istringstream stm(s);
T result;
stm >> result;
if(stm.tellg() != s.size())
throw error;
return result;
}
This can help you to convert any string to any type such as float, int, double...
Yes, you can store the integer directly:
int num = 45;
If you must parse a string, atoi or strol is going to win the "shortest amount of code" contest.
strtol() actually requires a fair amount of code. It can return LONG_MIN or LONG_MAX either if that's the actual converted value or if there's an underflow or overflow, and it can return 0 either if that's the actual value or if there was no number to convert. You need to set errno = 0 before the call, and check the endptr.