strtoint stoi parse not error declared convert atol c++ standards language-lawyer

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 cuando endptr 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" ?