¿Cómo convertir una cadena a entero en C?

11 minutos de lectura

avatar de usuario
usuario618677

Estoy tratando de averiguar si hay una forma alternativa de convertir cadenas en 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 manera?

  • @Yann, perdón por esa confusión. Preferiré C.

    – usuario618677

    11 de agosto de 2011 a las 16:16

  • programaciónsimplificada.com/c/source-code/…

    -Kyle Bridenstine

    28 de septiembre de 2014 a las 1:07

  • Funciona, pero no es la forma recomendada, porque no hay forma de manejar los errores. Nunca use esto en el código de producción a menos que pueda confiar en la entrada al 100%.

    –Uwe Geuder

    5 de noviembre de 2015 a las 17:47

  • @EJP Solo para mejorar.

    – usuario618677

    3 de agosto de 2018 a las 1:32

avatar de usuario
cnicutar

Hay strtol que es mejor en mi opinión. También he tomado un gusto en strtonumasí 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);

Usted también puede estar interesado en strtoumax y strtoimax que son 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, aléjate 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 no está definido..

  • ¿Qué necesito incluir para strtonum? Sigo recibiendo una advertencia de declaración implícita

    – jsj

    30 de marzo de 2013 a las 1:57


  • @trideceth12 En los sistemas donde está disponible, debe declararse en #<stdlib.h>. Sin embargo, podría usar el estándar strtoumax alternativa.

    – cnicutar

    30 de marzo de 2013 a las 10:21

  • Esta respuesta no parece más corta que el primer código del interrogador.

    – Azurespot

    3 de septiembre de 2014 a las 4:46

  • @NoniA. La concisión siempre es buena, pero no a expensas de la corrección.

    – cnicutar

    3 de septiembre de 2014 a las 9:33

  • No es tan malo como inseguro. atoi() funciona si la entrada es válida. Pero, ¿y si haces atoi (“gato”)? strtol() tiene un comportamiento definido si el valor no se puede representar como largo, atoi() no.

    – Daniel B.

    22 de julio de 2015 a las 19:13

avatar de usuario
Hormiga

No use funciones de ato... grupo. Estos están rotos y prácticamente inútiles. Una solución moderadamente mejor sería usar sscanfaunque tampoco es perfecto.

Para convertir una cadena en un entero, las funciones de strto... debe utilizarse el grupo. En tu caso concreto 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)).

    –Keith Thompson

    11 de agosto de 2011 a las 6:50

  • @Keith Thompson: Eso es exactamente lo que quiero decir. atoi no proporciona comentarios significativos de éxito/fracaso y tiene un comportamiento indefinido en caso de desbordamiento. sscanf proporciona una especie de retroalimentación de éxito/fracaso (el valor de retorno, que es lo que lo hace “moderadamente mejor”), pero aún tiene un comportamiento indefinido en el desbordamiento. Solamente strtol es una solución viable.

    – Ant

    11 de agosto de 2011 a las 6:55


  • Acordado; Solo quería enfatizar el problema potencialmente fatal con sscanf. (Aunque confieso que a veces uso atoigeneralmente para programas que no espero que sobrevivan más de 10 minutos antes de eliminar la fuente).

    –Keith Thompson

    11 de agosto de 2011 a las 6:58

avatar de usuario
Biswajit Karmakar

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;
}

Como ya se mencionó, el atoi La familia de funciones nunca debe usarse en ningún programa C, ya que no tienen ningún manejo de errores.

La la strtol La familia de funciones es 100% equivalente, pero con una funcionalidad extendida: tiene manejo de errores y también admite otras bases además del decimal, como hexadecimal o binaria. Por lo tanto, la respuesta correcta es: usar strtol (familia).

Si por alguna razón insiste en implementar esta función manualmente, debe intentar hacer algo similar a strtol en caso de que haya otros símbolos presentes además del signo y los dígitos opcionales. Es bastante común que queramos convertir números que son parte de una cadena más grande, por ejemplo.

Una versión ingenua con soporte para el manejo de errores podría parecerse al siguiente ejemplo. Este código es solo para números decimales en base 10, pero por lo demás se comporta como strtol con un puntero opcional configurado para apuntar al primer símbolo inválido encontrado (si lo hay). También tenga en cuenta que este código no maneja los desbordamientos.

#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;
}

Para manejar los desbordamientos, se puede, por ejemplo, agregar un código que cuente los caracteres y verificar que nunca pasen de 10, suponiendo que sean de 32 bits. long que puede ser máximo 214748364710 dígitos.

avatar de usuario
Ciro Santilli Путлер Капут 六四事

Robusto C89 strtolsolución basada en

Con:

  • ningún comportamiento indefinido (como se podría tener con el atoi familia)
  • una definición más estricta de entero que strtol (por ejemplo, sin espacios en blanco iniciales ni caracteres basura finales)
  • clasificación del caso de error (por ejemplo, para dar mensajes de error útiles a los usuarios)
  • un “conjunto de pruebas”
#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;
}

GitHub ascendente.

Basado en: https://stackoverflow.com/a/6154614/895245

  • Bonito robusto str2int(). Pedante: uso isspace((unsigned char) s[0]).

    – chux – Reincorporar a Monica

    30 de mayo de 2016 a las 12:09

  • @chux gracias! ¿Puedes explicar un poco más por qué el (unsigned char) ¿El elenco podría marcar la diferencia?

    – Ciro Santilli Путлер Капут 六四事

    30 mayo 2016 a las 13:00

  • El compilador IAR C advierte que l > INT_MAX y l < INT_MIN son comparaciones de enteros inútiles ya que cualquiera de los resultados es siempre falso. ¿Qué pasa si los cambio a l >= INT_MAX y l <= INT_MIN para borrar las advertencias? En el BRAZO C, largo y En t son de 32 bits firmados Tipos de datos básicos en ARM C y C++

    – ecle

    26 de enero de 2017 a las 2:23


  • @ecle cambiando el código a l >= INT_MAX incurre en una funcionalidad incorrecta: Ejemplo de retorno STR2INT_OVERFLOW con entrada "32767" y 16 bits int. Utilice una compilación condicional. Ejemplo.

    – chux – Reincorporar a Monica

    20 de diciembre de 2017 a las 12:45

  • 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 el uso del código de llamada errno sobre int fuera de rango. Igual por if (l < INT_MIN....

    – chux – Reincorporar a Monica

    22/09/2018 a las 17:44


avatar de usuario
Neuron – Libertad para Ucrania

puedes codificar 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, que puede plegarse en 3 líneas.

  • Bonito robusto str2int(). Pedante: uso isspace((unsigned char) s[0]).

    – chux – Reincorporar a Monica

    30 de mayo de 2016 a las 12:09

  • @chux gracias! ¿Puedes explicar un poco más por qué el (unsigned char) ¿El elenco podría marcar la diferencia?

    – Ciro Santilli Путлер Капут 六四事

    30 mayo 2016 a las 13:00

  • El compilador IAR C advierte que l > INT_MAX y l < INT_MIN son comparaciones de enteros inútiles ya que cualquiera de los resultados es siempre falso. ¿Qué pasa si los cambio a l >= INT_MAX y l <= INT_MIN para borrar las advertencias? En el BRAZO C, largo y En t son de 32 bits firmados Tipos de datos básicos en ARM C y C++

    – ecle

    26 de enero de 2017 a las 2:23


  • @ecle cambiando el código a l >= INT_MAX incurre en una funcionalidad incorrecta: Ejemplo de retorno STR2INT_OVERFLOW con entrada "32767" y 16 bits int. Utilice una compilación condicional. Ejemplo.

    – chux – Reincorporar a Monica

    20 de diciembre de 2017 a las 12:45

  • 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 el uso del código de llamada errno sobre int fuera de rango. Igual por if (l < INT_MIN....

    – chux – Reincorporar a Monica

    22/09/2018 a las 17:44


avatar de usuario
jacob

Solo quería compartir una solución para el 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;
}

  • No maneja el desbordamiento. Además, el parámetro debe ser const char *.

    – Roland Illig

    21 de enero de 2017 a las 17:35


  • Además, ¿qué es eso? 48 ¿significar? ¿Estás asumiendo que ese es el valor de '0' ¿Dónde se ejecutará el código? ¡Por favor, no inflijas suposiciones tan amplias en el mundo!

    –Toby Speight

    18 de junio de 2018 a las 16:15

  • @TobySpeight Sí, supongo que 48 representan ‘0’ en la tabla ascii.

    – jacob

    20 de junio de 2018 a las 9:17

  • No todo el mundo es ASCII, solo use '0' como deberías

    –Toby Speight

    20 de junio de 2018 a las 10:42

  • se recomienda utilizar el strtoul función en su lugar.

    – reloj rápido

    1 de marzo de 2020 a las 6:51


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad