Estoy tratando de averiguar si hay una forma alternativa de convertir cadenas a enteros en C.
Regularmente modelo lo siguiente en mi código.
char s[] = "45";
int num = atoi(s);
Entonces, ¿hay una mejor manera u otra?
Estoy tratando de averiguar si hay una forma alternativa de convertir cadenas a enteros en C.
Regularmente modelo lo siguiente en mi código.
char s[] = "45";
int num = atoi(s);
Entonces, ¿hay una mejor manera u otra?
Respuestas:
Hay strtol
cuál es mejor IMO. También me ha gustado mucho strtonum
, así que úsalo si lo tienes (pero recuerda que no es portátil):
long long
strtonum(const char *nptr, long long minval, long long maxval,
const char **errstr);
También puede interesarle strtoumax
ystrtoimax
cuáles son las funciones estándar en C99. Por ejemplo, podrías decir:
uintmax_t num = strtoumax(s, NULL, 10);
if (num == UINTMAX_MAX && errno == ERANGE)
/* Could not convert. */
De todos modos, mantente alejado de atoi
:
La llamada atoi (str) será equivalente a:
(int) strtol(str, (char **)NULL, 10)
excepto que el manejo de errores puede diferir. Si el valor no se puede representar, el comportamiento es indefinido .
strtonum
? Sigo recibiendo una advertencia de declaración implícita
#<stdlib.h>
. Sin embargo, podría usar la strtoumax
alternativa estándar .
Solución robusta strtol
basada en C89
Con:
atoi
familia)strtol
(por ejemplo, sin espacios en blanco iniciales ni caracteres de basura finales)#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(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;
}
Basado en: https://stackoverflow.com/a/6154614/895245
str2int()
. Pedantic: uso isspace((unsigned char) s[0])
.
(unsigned char)
elenco podría marcar la diferencia?
l > INT_MAX
y l < INT_MIN
son una comparación de enteros sin sentido ya que cualquiera de los resultados es siempre falso. ¿Qué sucede si los cambio l >= INT_MAX
y l <= INT_MIN
borro las advertencias? En ARM C, long e int son tipos de datos básicos con
if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) return STR2INT_OVERFLOW;
sería mejor como if (l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) { errno = ERANGE; return STR2INT_OVERFLOW;}
para permitir llamar a código para utilizar errno
en int
fuera de rango. Lo mismo para if (l < INT_MIN...
.
No uses funciones del ato...
grupo. Estos están rotos y prácticamente inútiles. Sería una solución moderadamente mejor sscanf
, aunque tampoco es perfecta.
Para convertir una cadena en un entero, se strto...
deben usar las funciones del grupo. En su caso específico sería strtol
función.
sscanf
en realidad tiene un comportamiento indefinido si intenta convertir un número fuera del rango de su tipo (por ejemplo, sscanf("999999999999999999999", "%d", &n)
).
atoi
no proporciona comentarios significativos de éxito / fracaso y tiene un comportamiento indefinido en caso de desbordamiento. sscanf
proporciona comentarios de éxito / fracaso (el valor de retorno, que es lo que lo hace "moderadamente mejor"), pero aún tiene un comportamiento indefinido en caso de desbordamiento. Solo strtol
es una solución viable.
sscanf
. (Aunque confieso que a veces uso atoi
, generalmente para programas que no espero sobrevivir más de 10 minutos antes de eliminar la fuente.)
Puedes codificar un poco de atoi () por diversión:
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);
}
También puede hacerlo recursivo, lo que puede envejecer en 3 líneas =)
code
((* str) - '0')code
"----1"
2) Tiene un comportamiento indefinido con int
desbordamiento cuando el resultado debería ser INT_MIN
. Consideremy_getnbr("-2147483648")
Solo quería compartir una solución por tiempo sin firmar también.
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
significa eso ? ¿Asume que ese es el valor de '0'
dónde se ejecutará el código? ¡Por favor, no infundas suposiciones tan amplias en el mundo!
'0'
como deberías.
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;
}
¡Siempre puedes rodar el tuyo!
#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;
}
Esto hará lo que quieras sin desorden.
strto...
familia de funciones. Son portátiles y significativamente mejores.
0x2d, 0x30
lugar de '-', '0'
. No permite '+'
firmar. ¿Por qué (int)
echarlo (int)strlen(snum)
? UB si la entrada es ""
. UB cuando el resultado se INT_MIN
deba a int
desbordar conaccum += (snum[strIdx] - 0x30) * pow(10, idx);
Esta función te ayudará
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
y amigos es que si hay un desbordamiento, es un comportamiento indefinido. Su función no verifica esto. strtol
y amigos lo hacen.
Ok, tuve el mismo problema. Se me ocurrió esta solución. Funcionó mejor para mí. Intenté con atoi () pero no funcionó bien para mí. Así que aquí está mi solución:
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;
}
//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;
}
nInt = (nInt *= 10) + ((int) snum[index] - 48);
vs. nInt = nInt*10 + snum[index] - '0';
if(!nInt)
no es necesario.
En C ++, puede usar una función de este tipo:
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;
}
Esto puede ayudarlo a convertir cualquier cadena a cualquier tipo, como float, int, double ...
Sí, puede almacenar el número entero directamente:
int num = 45;
Si debe analizar una cadena atoi
o strol
va a ganar el concurso de "menor cantidad de código".
strtol()
realidad requiere una buena cantidad de código. Puede volver LONG_MIN
o LONG_MAX
bien si ese es el valor convertido actual o si hay un desbordamiento o desbordamiento, y puede devolver 0 o bien si ese es el valor real o si no había un número para convertir. Debe configurar errno = 0
antes de la llamada y verificar el endptr
.