parse - stoi in c++
¿Es std:: stoi realmente seguro de usar? (1)
¿ std::stoi
arroja un error en la entrada "abcxyz"
?
Sí.
Creo que su confusión puede provenir del hecho de que strtol
nunca informa un error, excepto en caso de desbordamiento. Puede informar que no se realizó ninguna conversión, pero esto nunca se conoce como una condición de error en el estándar C.
strtol
se define de manera similar por los tres estándares de C, y le ahorraré detalles aburridos, pero básicamente define una "secuencia de asunto" que es una subcadena de la cadena de entrada correspondiente al número real. Las siguientes cuatro condiciones son equivalentes:
- la secuencia del tema tiene la forma esperada (en inglés simple: es un número)
- la secuencia del sujeto no está vacía
- una conversión ha ocurrido
-
*endptr != nptr
(esto solo tiene sentido cuandoendptr
no es nulo)
Cuando hay un desbordamiento, se dice que la conversión se ha producido.
Ahora, está bastante claro que debido a que "abcxyz"
no contiene un número, la secuencia del sujeto de la cadena "abcxyz"
debe estar vacía, por lo que no se puede realizar ninguna conversión. El siguiente programa C90 / C99 / C11 lo confirmará experimentalmente:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *nptr = "abcxyz", *endptr[1];
strtol(nptr, endptr, 0);
if (*endptr == nptr)
printf("No conversion could be performed./n");
return 0;
}
Esto implica que cualquier implementación conforme de std::stoi
debe arrojar invalid_argument
cuando se le da la entrada "abcxyz"
sin un argumento base opcional.
¿Esto significa que std::stoi
tiene una comprobación de errores satisfactoria?
No. La persona con la que estaba hablando es correcta cuando dice que std::stoi
es más indulgente que la comprobación completa errno == 0 && end != start && *end==''/0''
after std::strtol
, porque std::stoi
silenciosamente todos los caracteres a partir del primer carácter no numérico de la cadena.
De hecho, el único lenguaje cuya conversión nativa se comporta de forma parecida a std::stoi
es Javascript, e incluso entonces debes forzar la base 10 con parseInt(n, 10)
para evitar el caso especial de números hexadecimales:
input | std::atoi std::stoi Javascript full check
===========+=============================================================
hello | 0 error error(NaN) error
0xygen | 0 0 error(NaN) error
0x42 | 0 0 66 error
42x0 | 42 42 42 error
42 | 42 42 42 42
-----------+-------------------------------------------------------------
languages | Perl, Ruby, Javascript Javascript C#, Java,
| PHP, C... (base 10) Python...
Nota: también hay diferencias entre los lenguajes en el manejo de espacios en blanco y signos redundantes.
Ok, entonces quiero una verificación completa de errores, ¿qué debo usar?
No conozco ninguna función incorporada que haga esto, pero boost::lexical_cast<int>
hará lo que quiera. Es particularmente estricto ya que incluso rechaza el espacio en blanco circundante, a diferencia de la función int()
Python. Tenga en cuenta que los caracteres no válidos y los desbordamientos producen la misma excepción, boost::bad_lexical_cast
.
#include <boost/lexical_cast.hpp>
int main() {
std::string s = "42";
try {
int n = boost::lexical_cast<int>(s);
std::cout << "n = " << n << std::endl;
} catch (boost::bad_lexical_cast) {
std::cout << "conversion failed" << std::endl;
}
}
Tuve una conversación encantadora con alguien sobre las caídas de std::stoi
. Para decirlo sin rodeos, usa std::strtol
internamente, y arroja si eso informa un error. Según ellos, sin embargo, std::strtol
no debe informar un error para una entrada de "abcxyz"
, lo que hace que stoi
no lance std::invalid_argument
.
En primer lugar, aquí hay dos programas probados en GCC sobre el comportamiento de estos casos:
strtol
stoi
Ambos muestran éxito en "123"
y falla en "abc"
.
Busqué en el estándar para obtener más información:
§ 21.5
Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that
no conversion could be performed. Throws out_of_range if the converted value is
outside the range of representable values for the return type.
Eso resume el comportamiento de confiar en strtol
. Ahora que hay de strtol
? Encontré esto en el borrador C11:
§7.22.1.4
If the subject sequence is empty or does not have the expected form, no
conversion is performed; the value of nptr is stored in the object
pointed to by endptr, provided that endptr is not a null pointer.
Dada la situación de pasar "abc"
, el estándar C dicta que nptr
, que apunta al comienzo de la cadena, se almacenará en endptr
, el puntero endptr
. Esto parece consistente con la prueba. Además, se debe devolver 0, como se indica en este:
§7.22.1.4
If no conversion could be performed, zero is returned.
La referencia anterior decía que no se realizaría ninguna conversión, por lo que debe devolver 0. Estas condiciones ahora cumplen con el estándar C ++ 11 para stoi
throwing std::invalid_argument
.
El resultado de esto me importa porque no quiero ir por ahí recomendando stoi
como una mejor alternativa a otros métodos de conversión de cadena a int, o usarlo yo mismo como si funcionara de la manera esperada, si no lo hace t captura texto como una conversión no válida.
Entonces, después de todo esto, ¿me equivoqué en algún lado? Me parece que tengo una buena prueba de que se ha lanzado esta excepción. ¿Mi prueba es válida o std::stoi
no garantiza arrojar esa excepción cuando se le da "abc"
?