¿Cómo analizar una cadena a un int en C++?

11 minutos de lectura

¿Como analizar una cadena a un int en C
eugenio yokota

¿Cuál es la forma C++ de analizar una cadena (dada como char *) en un int? El manejo de errores robusto y claro es una ventaja (en lugar de devolviendo cero).

  • ¿Qué tal algunos de los ejemplos de los siguientes: codeproject.com/KB/recipes/Tokenizer.aspx Son muy eficientes y algo elegantes.

    Matthieu N.

    4 de noviembre de 2010 a las 1:52

  • @Beh Tou Cheh, si cree que es una buena manera de analizar int, publíquelo como respuesta.

    – Eugene Yokota

    4 de noviembre de 2010 a las 14:57

  • Lo mismo para C: stackoverflow.com/questions/7021725/…

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

    12 de agosto de 2014 a las 16:29

1647576611 492 ¿Como analizar una cadena a un int en C
Dan moldeado

Qué no hacer

Este es mi primer consejo: no use stringstream para esto. Si bien al principio puede parecer fácil de usar, encontrará que tiene que hacer mucho trabajo adicional si desea robustez y un buen manejo de errores.

Aquí hay un enfoque que intuitivamente parece que debería funcionar:

bool str2int (int &i, char const *s)
{
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail()) {
        // not an integer
        return false;
    }
    return true;
}

Esto tiene un gran problema: str2int(i, "1337h4x0r") felizmente volveré true y i obtendrá el valor 1337. Podemos solucionar este problema asegurándonos de que no haya más caracteres en el stringstream después de la conversión:

bool str2int (int &i, char const *s)
{
    char              c;
    std::stringstream ss(s);
    ss >> i;
    if (ss.fail() || ss.get(c)) {
        // not an integer
        return false;
    }
    return true;
}

Solucionamos un problema, pero todavía hay un par de otros problemas.

¿Qué pasa si el número en la cadena no es base 10? Podemos intentar acomodar otras bases configurando la transmisión en el modo correcto (por ejemplo, ss << std::hex) antes de intentar la conversión. Pero esto significa que la persona que llama debe saber a priori ¿En qué base está el número y cómo puede saberlo la persona que llama? La persona que llama aún no sabe cuál es el número. Ni siquiera saben que es es ¡un número! ¿Cómo se puede esperar que sepan qué base es? Podríamos simplemente ordenar que todos los números ingresados ​​en nuestros programas deben ser de base 10 y rechazar la entrada hexadecimal u octal como inválida. Pero eso no es muy flexible ni robusto. No existe una solución sencilla para este problema. No puede simplemente intentar la conversión una vez para cada base, porque la conversión decimal siempre tendrá éxito para los números octales (con un cero al frente) y la conversión octal puede tener éxito para algunos números decimales. Así que ahora tienes que comprobar si hay un cero a la izquierda. ¡Pero espera! Los números hexadecimales también pueden comenzar con un cero inicial (0x…). Suspiro.

Incluso si logra resolver los problemas anteriores, todavía hay otro problema mayor: ¿qué pasa si la persona que llama necesita distinguir entre una entrada incorrecta (por ejemplo, “123foo”) y un número que está fuera del rango de int (por ejemplo, “4000000000” para 32 bits int)? Con stringstream, no hay manera de hacer esta distinción. Solo sabemos si la conversión tuvo éxito o falló. Si falla, no tenemos forma de saberlo. por qué Falló. Como puedes ver, stringstream deja mucho que desear si desea robustez y un manejo claro de errores.

Esto me lleva a mi segundo consejo: no use Boost lexical_cast para esto. Considere lo que el lexical_cast la documentación tiene que decir:

Cuando se requiere un mayor grado de control sobre las conversiones, std::stringstream y std::wstringstream ofrecen una ruta más adecuada. Cuando se requieren conversiones no basadas en secuencias, lexical_cast es la herramienta incorrecta para el trabajo y no es un caso especial para tales escenarios.

¿¿Qué?? eso ya lo hemos visto stringstream tiene un bajo nivel de control y, sin embargo, dice stringstream debe usarse en lugar de lexical_cast si necesita “un mayor nivel de control”. También porque lexical_cast es solo un envoltorio stringstreamsufre de los mismos problemas que stringstream hace: soporte deficiente para múltiples bases numéricas y manejo de errores deficiente.

La mejor solucion

Afortunadamente, alguien ya ha resuelto todos los problemas anteriores. La biblioteca estándar de C contiene strtol y familiares que no tienen ninguno de estos problemas.

enum STR2INT_ERROR { SUCCESS, OVERFLOW, UNDERFLOW, INCONVERTIBLE };

STR2INT_ERROR str2int (int &i, char const *s, int base = 0)
{
    char *end;
    long  l;
    errno = 0;
    l = strtol(s, &end, base);
    if ((errno == ERANGE && l == LONG_MAX) || l > INT_MAX) {
        return OVERFLOW;
    }
    if ((errno == ERANGE && l == LONG_MIN) || l < INT_MIN) {
        return UNDERFLOW;
    }
    if (*s == '\0' || *end != '\0') {
        return INCONVERTIBLE;
    }
    i = l;
    return SUCCESS;
}

Bastante simple para algo que maneja todos los casos de error y también admite cualquier base numérica del 2 al 36. Si base es cero (el valor predeterminado) intentará convertir desde cualquier base. O la persona que llama puede proporcionar el tercer argumento y especificar que la conversión solo debe intentarse para una base en particular. Es robusto y maneja todos los errores con una cantidad mínima de esfuerzo.

Otras razones para preferir strtol (y familia):

  • Se exhibe mucho mejor rendimiento en tiempo de ejecución
  • Introduce menos sobrecarga en el tiempo de compilación (los otros extraen casi 20 veces más SLOC de los encabezados)
  • Da como resultado el tamaño de código más pequeño.

No hay absolutamente ninguna buena razón para usar cualquier otro método.

  • @JamesDunne: POSIX requiere strtol para ser seguro para subprocesos. POSIX también requiere errno para utilizar el almacenamiento local de subprocesos. Incluso en sistemas que no son POSIX, casi todas las implementaciones de errno en sistemas multiproceso, use el almacenamiento local de subprocesos. El último estándar de C++ requiere errno para ser compatible con POSIX. El último estándar C también requiere errno tener almacenamiento local de subprocesos. Incluso en Windows, que definitivamente no es compatible con POSIX, errno es seguro para subprocesos y, por extensión, también lo es strtol.

    – Moldeado Dan

    20 de abril de 2012 a las 11:34


  • Realmente no puedo seguir tu razonamiento contra el uso de boost::lexical_cast. Como dicen, std::stringstream ofrece mucho control: usted mismo hace todo, desde la verificación de errores hasta la determinación de la base. La documentación actual lo expresa así: “Para conversiones más complicadas, como cuando la precisión o el formato necesitan un control más estricto que el que ofrece el comportamiento predeterminado de lexical_cast, se recomienda el enfoque convencional std::stringstream”.

    – fhd

    6 de mayo de 2012 a las 4:21


  • Esta es una codificación C inapropiada dentro de C++. La biblioteca estándar contiene std::stol para esto, que arrojará excepciones de manera apropiada en lugar de devolver constantes.

    – Fuzzy Tew

    01/08/2013 a las 15:40

  • @fuzzyTew Escribí esta respuesta antes std::stol incluso se agregó al lenguaje C++. Dicho esto, no creo que sea justo decir que esto es “codificación C dentro de C++”. es tonto decir eso std::strtol es codificación C cuando forma parte explícitamente del lenguaje C++. Mi respuesta se aplicó perfectamente a C ++ cuando se escribió y todavía se aplica incluso con el nuevo std::stol. Llamar a funciones que pueden generar excepciones no siempre es lo mejor para cada situación de programación.

    – Moldeado Dan

    02/09/2013 a las 20:33


  • @fuzzyTew: Quedarse sin espacio en disco es una condición excepcional. Los archivos de datos con formato incorrecto producidos por computadora son una condición excepcional. Pero los errores tipográficos en la entrada del usuario no son excepcionales. Es bueno tener un enfoque de análisis que sea capaz de manejar fallas de análisis normales y no excepcionales.

    – Ben Voigt

    20 de febrero de 2015 a las 17:24

1647576611 674 ¿Como analizar una cadena a un int en C
CC.

En el nuevo C++11 hay funciones para eso: stoi, stol, stoll, stoul, etc.

int myNr = std::stoi(myString);

Lanzará una excepción en caso de error de conversión.

Incluso estas nuevas funciones todavía tienen la mismo problema como señaló Dan: felizmente convertirán la cadena “11x” en el número entero “11”.

Ver más: http://en.cppreference.com/w/cpp/string/basic_string/stol

  • Pero aceptan argumentos además de eso, uno de ellos es un punto para size_t que, si no es nulo, se establece en el primer carácter no convertido

    – Zharf

    13 de octubre de 2012 a las 10:36

  • Sí, usando el segundo parámetro de std::stoi puede detectar entradas no válidas. Sin embargo, todavía tiene que lanzar su propia función de conversión …

    – CC.

    13 de diciembre de 2012 a las 20:56

  • Al igual que lo hizo la respuesta aceptada, pero con estas funciones estándar que serían mucho más limpias, en mi opinión

    – Zharf

    13 de diciembre de 2012 a las 22:33

  • Tenga en cuenta que el segundo argumento en estas funciones se puede usar para saber si la cadena completa se convirtió o no. Si el resultado size_t no es igual a la longitud de la cadena, entonces se detuvo antes de tiempo. Todavía devolverá 11 en ese caso, pero pos será 2 en lugar de la longitud de la cadena 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29

    – Zoe apoya a Ucrania

    22 de junio de 2020 a las 12:11

¿Como analizar una cadena a un int en C
luka marinko

Esta es una forma C más segura que atoi()

const char* str = "123";
int i;

if(sscanf(str, "%d", &i)  == EOF )
{
   /* error */
}

C++ con biblioteca estándar flujo de cuerdas: (gracias CMS)

int str2int (const string &str) {
  stringstream ss(str);
  int num;
  if((ss >> num).fail())
  { 
      //ERROR 
  }
  return num;
}

Con aumentar biblioteca: (gracias jk)

#include <boost/lexical_cast.hpp>
#include <string>

try
{
    std::string str = "123";
    int number = boost::lexical_cast< int >( str );
}
catch( const boost::bad_lexical_cast & )
{
    // Error
}

Editar: se corrigió la versión de stringstream para que maneje los errores. (gracias al comentario de CMS y jk en la publicación original)

  • actualice su versión de stringstream para incluir una verificación de stringstream::fail() (según lo solicitado por el interrogador “Manejo de errores sólido y claro”)

    – jk.

    11 de octubre de 2008 a las 19:54

  • Tu versión de stringstream aceptará cosas como “10haha” sin quejarse

    – Johannes Schaub – litb

    13 de noviembre de 2008 a las 13:40

  • cámbielo a (!(ss >> num).fail() && (ss >> ws).eof()) de ((ss >> num).fail()) si desea el mismo manejo como lexical_cast

    – Johannes Schaub – litb

    13 de noviembre de 2008 a las 13:42

  • El C++ con el método stringstream de biblioteca estándar no funciona para cadenas como “12-SomeString” incluso con la verificación .fail().

    – captonssj

    12 de noviembre de 2009 a las 15:20

  • C ++ 11 incluye funciones rápidas estándar para esto ahora

    – Fuzzy Tew

    1 de agosto de 2013 a las 15:55

La buena ‘vieja forma C todavía funciona. Recomiendo strtol o strtoul. Entre el estado de retorno y el ‘endPtr’, puede dar un buen resultado de diagnóstico. También maneja múltiples bases muy bien.

1647576612 951 ¿Como analizar una cadena a un int en C
jk.

Puedes usar impulso lexical_castque envuelve esto en una interfaz más genérica.
lexical_cast<Target>(Source) lanza bad_lexical_cast sobre el fracaso

  • Boost lexical_cast es extremadamente lento y dolorosamente ineficiente.

    Matthieu N.

    4 de noviembre de 2010 a las 1:37

  • @Matthieu Las actualizaciones de Boost han mejorado bastante el rendimiento: boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/… (ver también stackoverflow.com/questions/1250795/… )

    – moscas

    10 mayo 2012 a las 15:11

1647576612 951 ¿Como analizar una cadena a un int en C
jk.

Puede usar un stringstream de la biblioteca estándar de C++:

stringstream ss(str);
int x;
ss >> x;

if(ss) { // <-- error handling
  // use x
} else {
  // not a number
}

El estado de flujo se configurará para fallar si se encuentra un dígito que no sea al intentar leer un número entero.

Ver Trampas de la corriente para las trampas del manejo de errores y flujos en C++.

  • Boost lexical_cast es extremadamente lento y dolorosamente ineficiente.

    Matthieu N.

    4 de noviembre de 2010 a las 1:37

  • @Matthieu Las actualizaciones de Boost han mejorado bastante el rendimiento: boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/… (ver también stackoverflow.com/questions/1250795/… )

    – moscas

    10 mayo 2012 a las 15:11

Desde C++17 en adelante puedes usar std::from_chars desde el <charconv> encabezado como se documenta aquí.

Por ejemplo:

#include <iostream>
#include <charconv>
#include <array>

int main()
{
    char const * str = "42";
    int value = 0;

    std::from_chars_result result = std::from_chars(std::begin(str), std::end(str), value);

    if(result.error == std::errc::invalid_argument)
    {
      std::cout << "Error, invalid format";
    }
    else if(result.error == std::errc::result_out_of_range)
    {
      std::cout << "Error, value too big for int range";
    }
    else
    {
      std::cout << "Success: " << result;
    }
}

Como beneficio adicional, también puede manejar otras bases, como hexadecimal.

¿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