259 votos

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

¿Qué es la manera de C++ de analizar una cadena (como char *) en un int? Manejo de errores robusta y clara es un plus (en lugar de volver a cero).

204voto

Dan Moulding Puntos 46866

Qué no hacer

Aquí está mi primer consejo: no use stringstream para esto. Mientras al principio puede parecer sencillo de utilizar, usted encontrará que usted tiene que hacer un montón de trabajo extra si desea robustez y buen manejo de errores.

Aquí es 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") estará feliz de regresar true y i obtendrá el valor 1337. Podemos evitar este problema asegurándose de que no hay más personajes en la 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;
}

Se ha solucionado un problema, pero todavía hay un par de otros problemas.

¿Qué pasa si el número en la cadena no es en base 10? Podemos tratar de dar cabida a otras bases, estableciendo la secuencia en el modo correcto (ej. ss << std::hex) antes de intentar la conversión. Pero esto significa que la persona que llama debe saber a priori qué base el número es-y ¿cómo puede la persona que llama puede saber de que? La persona que llama no sabe lo que el número es todavía. No saben ni siquiera que es un número! ¿Cómo pueden esperar a saber qué base? Podríamos mandato de que todos los números de entrada a nuestros programas deben ser la base 10 y rechazar hexadecimal u octal entrada no válida. Pero eso no es muy flexibles o sólidas. No existe una solución simple a este problema. Simplemente no se puede intentar la conversión de una vez para cada base, debido a la conversión de decimal siempre tendrá éxito para los números octales (con un cero a la izquierda) y la conversión octal puede tener éxito para algunos números decimales. Así que ahora usted tiene que comprobar un cero a la izquierda. Pero espera! Números hexadecimales puede comenzar con un cero (0...). Suspiro.

Incluso si usted tiene éxito en el trato con los problemas anteriores, todavía hay otro problema mayor: ¿qué pasa si la persona necesita para distinguir entre el mal de entrada (p. ej. "123foo") y un número que está fuera del rango de int (p. ej. "4000000000" para 32-bit int)? Con stringstream, no hay ninguna manera de hacer esta distinción. Sólo sabemos si la conversión se realizó correctamente o no. Si falla, no tenemos manera de saber por qué falló. Como se puede ver, stringstream deja mucho que desear si desea robustez y claro el manejo de errores.

Esto me lleva a mi segundo consejo: no utilice Boost lexical_cast de este. Considere lo que el lexical_cast de la documentación que tiene que decir:

Donde un mayor grado de control es se requiere más de conversiones, std::stringstream y std::wstringstream ofrecer una más ruta de acceso apropiada. Donde no basada en secuencia de conversiones se requiere, lexical_cast es el mal herramienta para el trabajo y no es los casos especiales para este tipo de escenarios.

Qué?? Ya hemos visto que stringstream tiene un bajo nivel de control, y sin embargo dice stringstream debe ser utilizado en lugar de lexical_cast si usted necesita "un mayor nivel de control". También, porque lexical_cast es simplemente un contenedor alrededor stringstream, sufre de los mismos problemas que stringstream : pobre soporte para múltiples bases de pobres y manejo de errores.

La mejor solución

Afortunadamente, alguien ya ha resuelto todos los problemas anteriormente citados. La biblioteca estándar de C contiene strtol y de la familia 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 se encarga de todos los casos de error, y también es compatible con cualquier número de base 2 a 36. Si base es igual a cero (el valor predeterminado) intentará convertir a partir de cualquier base. O la persona que llama puede suministrar el tercer argumento y especificar que la conversión debe ser realizada únicamente para una determinada base. Es robusto y se ocupa de todos los errores con una cantidad mínima de esfuerzo.

Otras razones para preferir strtol (y la familia):

  • Presenta mucho mejor rendimiento en tiempo de ejecución
  • Introduce menos en tiempo de compilación de la cabeza (los otros tire en casi 20 veces más SLOC de encabezados)
  • Los resultados en el menor tamaño de código

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

162voto

CC. Puntos 502

En el nuevo C ++ 11 hay funciones para eso: stoi, stol, stoll, stoul y así sucesivamente.

int myNr = std::stoi(myString);

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

Incluso estas nuevas funciones todavía tienen el mismo problema como se ha señalado por Dan: felizmente convertirá la cadena "x 11" entero "11".

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

67voto

Luka Marinko Puntos 1184

Este es un seguro de C de manera que atoi()

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

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

C++ con la biblioteca estándar de stringstream: (gracias CMS )

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

Con el impulso de la 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
}

Edit: Corregido el stringstream versión, de modo que se encarga de los errores. (gracias a de la CMS y de jk comentario en el post original)

21voto

jk. Puntos 4421

Puede usar de Boost lexical_cast , que envuelve este en una interfaz más genérico. lexical_cast<Target>(Source) lanza bad_lexical_cast en caso de error.

21voto

Chris Arguin Puntos 6469

El bien ' vieja manera C todavía funciona. Te recomiendo strtol o strtoul. Entre el estado de devolución y el 'endPtr', usted puede dar buena salida de diagnóstico. También maneja múltiples bases muy bien.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X