¿Cuál es la forma en C ++ de analizar una cadena (dada como char *) en un int? El manejo de errores robusto y claro es un plus (en lugar de devolver cero )
261
¿Cuál es la forma en C ++ de analizar una cadena (dada como char *) en un int? El manejo de errores robusto y claro es un plus (en lugar de devolver cero )
Respuestas:
En el nuevo C ++ 11 hay funciones para eso: stoi, stol, stoll, stoul, etc.
Lanzará una excepción en el error de conversión.
Incluso estas nuevas funciones todavía tienen el mismo problema que señaló Dan: felizmente convertirán la cadena "11x" al número entero "11".
Ver más: http://en.cppreference.com/w/cpp/string/basic_string/stol
fuente
size_t
no es igual a la longitud de la cadena, entonces se detuvo antes. Todavía devolverá 11 en ese caso, peropos
será 2 en lugar de la longitud de la cadena 3. coliru.stacked-crooked.com/a/cabe25d64d2ffa29Qué no hacer
Aquí está mi primer consejo: no use stringstream para esto . Si bien al principio puede parecer simple de usar, encontrará que debe hacer mucho trabajo adicional si desea robustez y buen manejo de errores.
Aquí hay un enfoque que intuitivamente parece que debería funcionar:
Esto tiene un problema importante:
str2int(i, "1337h4x0r")
felizmente regresarátrue
yi
obtendrá el valor1337
. Podemos solucionar este problema asegurándonos de que no haya más caracteresstringstream
después de la conversión:Solucionamos un problema, pero todavía hay 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 qué base es el número, y ¿cómo puede saberlo? La persona que llama aún no sabe cuál es el número. Ni siquiera saben que es¡un número! ¿Cómo se puede esperar que sepan qué base es? Podríamos exigir que todos los números ingresados a nuestros programas deben ser de base 10 y rechazar el ingreso hexadecimal u octal como no válido. Pero eso no es muy flexible ni robusto. No hay una solución simple 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 inicial) y la conversión octal puede tener éxito para algunos números decimales. Así que ahora tienes que buscar 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é sucede 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 bitsint
)? Constringstream
, no hay forma de hacer esta distinción. Solo sabemos si la conversión tuvo éxito o falló. Si falla, no tenemos forma de saber por qué falló. Como puede ver,stringstream
deja mucho que desear si desea robustez y manejo claro de errores.Esto me lleva a mi segundo consejo: no use Boost
lexical_cast
para esto . Considere lo que lalexical_cast
documentación tiene que decir:¿¿Qué?? Ya hemos visto que
stringstream
tiene un bajo nivel de control y, sin embargo, dice questringstream
debe usarse en lugar delexical_cast
si necesita "un mayor nivel de control". Además, debido a quelexical_cast
es solo una envolturastringstream
, sufre los mismos problemas questringstream
tiene: pobre soporte para múltiples bases de números y pobre manejo de errores.La mejor solucion
Afortunadamente, alguien ya ha resuelto todos los problemas anteriores. La biblioteca estándar de C contiene una
strtol
familia que no tiene ninguno de estos problemas.Bastante simple para algo que maneja todos los casos de error y también admite cualquier base de números 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 particular. Es robusto y maneja todos los errores con un mínimo esfuerzo.Otras razones para preferir
strtol
(y familia):No hay absolutamente ninguna buena razón para usar ningún otro método.
fuente
strtol
ser seguro para subprocesos. POSIX también requiereerrno
usar almacenamiento local de subprocesos. Incluso en sistemas que no son POSIX, casi todas las implementaciones deerrno
sistemas multiproceso utilizan almacenamiento local de subprocesos. El último estándar C ++ requiereerrno
que sea compatible con POSIX. El último estándar C también requiereerrno
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 esstrtol
.std::stol
para esto, que arrojará excepciones en lugar de devolver constantes.std::stol
incluso antes de agregarla al lenguaje C ++. Dicho esto, no creo que sea justo decir que esto es "codificación C dentro de C ++". Es tonto decir questd::strtol
es codificación C cuando es explícitamente parte del lenguaje C ++. Mi respuesta se aplicó perfectamente a C ++ cuando se escribió y aún se aplica incluso con la nuevastd::stol
. Llamar a funciones que pueden generar excepciones no siempre es lo mejor para cada situación de programación.Esta es una forma C más segura que atoi ()
C ++ con secuencia de cadena de biblioteca estándar : (gracias CMS )
Con la biblioteca de impulso : (gracias jk )
Editar: se corrigió la versión de secuencia de cadena para que maneje los errores. (gracias al comentario de CMS y jk en la publicación original)
fuente
La buena y antigua forma de C todavía funciona. Recomiendo strtol o strtoul. Entre el estado de retorno y el 'endPtr', puede dar una buena salida de diagnóstico. También maneja múltiples bases muy bien.
fuente
Puedes usar Boost's
lexical_cast
, que envuelve esto en una interfaz más genérica.lexical_cast<Target>(Source)
tirabad_lexical_cast
al fracaso.fuente
Puede usar un flujo de cadena del libraray estándar de C ++:
Vea las trampas de Stream para las trampas de manejo de errores y streams en C ++.
fuente
Puedes usar stringstream's
fuente
Creo que estos tres enlaces lo resumen:
Las soluciones stringstream y lexical_cast son casi lo mismo que lexical cast está usando stringstream.
Algunas especializaciones del reparto léxico utilizan un enfoque diferente; consulte http://www.boost.org/doc/libs/release/boost/lexical_cast.hpp para obtener más detalles. Los enteros y flotantes ahora están especializados para la conversión de entero a cadena.
Uno puede especializarse en lexical_cast para sus propias necesidades y hacerlo rápido. Esta sería la solución definitiva para todas las partes, limpia y simple.
Los artículos ya mencionados muestran una comparación entre los diferentes métodos de conversión de enteros <-> cadenas. Los siguientes enfoques tienen sentido: viejo c-way, spirit.karma, fastformat, simple ingenuo loop.
Lexical_cast está bien en algunos casos, por ejemplo, para la conversión de int a string.
No es una buena idea convertir una cadena a int usando el reparto léxico, ya que es 10-40 veces más lento que atoi, dependiendo de la plataforma / compilador utilizado.
Boost.Spirit.Karma parece ser la biblioteca más rápida para convertir enteros en cadenas.
y el bucle simple básico del artículo mencionado anteriormente es una forma más rápida de convertir cadenas a int, obviamente no es la más segura, strtol () parece una solución más segura
fuente
La biblioteca de C ++ String Toolkit (StrTk) tiene la siguiente solución:
InputIterator puede ser de iteradores char *, char * o std :: string sin firmar, y se espera que T sea un int con signo, como con signo int, int o long
fuente
v = (10 * v) + digit;
se desborda innecesariamente con la entrada de cadena con el valor de texto deINT_MIN
. La tabla tiene un valor cuestionable frente a simplementedigit >= '0' && digit <= '9'
Si usted tiene 11 C ++, las soluciones adecuadas hoy en día son el C ++ número entero funciones de conversión en
<string>
:stoi
,stol
,stoul
,stoll
,stoull
. Lanzan excepciones apropiadas cuando se les da una entrada incorrecta y usan el rápido y pequeñostrto*
funciones debajo del capó.Si está atascado con una revisión anterior de C ++, sería portátil para usted imitar estas funciones en su implementación.
fuente
Desde C ++ 17 en adelante, puede usar
std::from_chars
desde el<charconv>
encabezado como se documenta aquí .Por ejemplo:
Como beneficio adicional, también puede manejar otras bases, como hexadecimal.
fuente
Me gusta la respuesta de Dan Moulding , solo le agregaré un poco de estilo C ++:
Funciona tanto para std :: string como const char * a través de la conversión implícita. También es útil para la conversión de base, por ejemplo, todos
to_int("0x7b")
yto_int("0173")
yto_int("01111011", 2)
yto_int("0000007B", 16)
yto_int("11120", 3)
y yto_int("3L", 34);
devolvería 123.A diferencia de
std::stoi
esto funciona en pre-C ++ 11. También a diferenciastd::stoi
,boost::lexical_cast
ystringstream
arroja excepciones para cadenas extrañas como "123hohoho".NB: esta función tolera los espacios iniciales pero no los espacios finales, es decir,
to_int(" 123")
devuelve 123 mientrasto_int("123 ")
lanza una excepción. Asegúrese de que esto sea aceptable para su caso de uso o ajuste el código.Dicha función podría ser parte de STL ...
fuente
Conozco tres formas de convertir String en int:
Use la función stoi (String to int) o simplemente vaya con Stringstream, la tercera forma de conversión individual, el código está a continuación:
1er método
2do método
3.er método, pero no para una conversión individual
fuente
Me gusta la respuesta de Dan , especialmente por evitar excepciones. Para el desarrollo de sistemas integrados y otros desarrollos de sistemas de bajo nivel, es posible que no haya un marco de excepción adecuado disponible.
Se agregó una marca de espacio en blanco después de una cadena válida ... estas tres líneas
También se agregó una comprobación de errores de análisis.
Aquí está la función completa.
fuente
" "
.strtol()
no se especifica para establecererrno
cuando no se produce conversión. Mejor usarif (s == end) return INCONVERTIBLE;
para detectar que no hay conversión. Y luegoif (*s == '\0' || *end != '\0')
puede simplificarse aif (*end)
2)|| l > LONG_MAX
y|| l < LONG_MIN
no sirve para nada: nunca son ciertas.Podrías usar este método definido.
Y si fuera a convertir de String a Integer, simplemente haría lo siguiente.
El resultado sería 102.
fuente
atoi
no parece "la forma C ++", a la luz de otras respuestas como la aceptadastd::stoi()
.Sé que esta es una pregunta anterior, pero me la he encontrado muchas veces y, hasta la fecha, todavía no he encontrado una solución bien diseñada que tenga las siguientes características:
Entonces, aquí está el mío, con una correa de prueba. Debido a que usa las funciones C strtoull / strtoll debajo del capó, siempre se convierte primero al tipo más grande disponible. Luego, si no está utilizando el tipo más grande, realizará comprobaciones de rango adicionales para verificar que su tipo no se haya desbordado. Para esto, es un poco menos eficaz que si uno eligiera correctamente strtol / strtoul. Sin embargo, también funciona para cortos / caracteres y, que yo sepa, no existe una función de biblioteca estándar que haga eso también.
Disfrutar; ojalá alguien lo encuentre útil.
StringToDecimal
es el método de usuario-tierra; está sobrecargado, por lo que puede llamarse así:o esto:
Odio repetir el tipo int, así que prefiero el último. Esto asegura que si el tipo de 'a' cambia, uno no obtendrá malos resultados. Desearía que el compilador pudiera resolverlo como:
... pero, C ++ no deduce los tipos de retorno de plantilla, así que eso es lo mejor que puedo obtener.
La implementación es bastante simple:
CstrtoxllWrapper
envuelve ambosstrtoull
ystrtoll
, llamando lo que sea necesario en función de la firma del tipo de plantilla y brindando algunas garantías adicionales (por ejemplo, la entrada negativa no se permite si no está firmada y asegura que se haya convertido toda la cadena).CstrtoxllWrapper
es utilizado porStringToSigned
yStringToUnsigned
con el tipo más grande (largo largo / sin signo largo largo) disponible para el compilador; Esto permite que se realice la conversión máxima. Luego, si es necesario,StringToSigned
/StringToUnsigned
realiza las comprobaciones de rango final en el tipo subyacente. Finalmente, el método del punto final,StringToDecimal
, decide a cuál de los métodos de plantilla StringTo * llamar en función de la firma del tipo subyacente.Creo que la mayoría de la basura puede ser optimizada por el compilador; casi todo debería ser determinista en tiempo de compilación. ¡Cualquier comentario sobre este aspecto sería interesante para mí!
fuente
long long
lugar deintmax_t
?if (ePtr != str)
. Además, useisspace((unsigned char) *ePtr)
para manejar adecuadamente los valores negativos de*ePtr
.En C, puedes usar
int atoi (const char * str)
,Analiza la cadena C-string interpretando su contenido como un número integral, que se devuelve como un valor de tipo int.
fuente
atoi
la pregunta, soy consciente de ello. La pregunta claramente no es sobre C, sino sobre C ++. -1