Hay una estándar seguro manera de convertir std::string_view
a int
?
Desde C++11 std::string
déjanos usar stoi
convertir a int
:
std::string str = "12345";
int i1 = stoi(str); // Works, have i1 = 12345
int i2 = stoi(str.substr(1,2)); // Works, have i2 = 23
try {
int i3 = stoi(std::string("abc"));
}
catch(const std::exception& e) {
std::cout << e.what() << std::endl; // Correctly throws 'invalid stoi argument'
}
Pero stoi
no soporta std::string_view
. Entonces, alternativamente, podríamos usar atoi
pero hay que tener mucho cuidado, por ejemplo:
std::string_view sv = "12345";
int i1 = atoi(sv.data()); // Works, have i1 = 12345
int i2 = atoi(sv.substr(1,2).data()); // Works, but wrong, have i2 = 2345, not 23
Entonces atoi
tampoco funciona, ya que se basa en el terminador nulo '\0'
(y por ejemplo sv.substr
no puede simplemente insertar/agregar uno).
Ahora, desde C++17 también hay from_chars
pero no parece fallar cuando se proporcionan entradas deficientes:
try {
int i3;
std::string_view sv = "abc";
std::from_chars(sv.data(), sv.data() + sv.size(), i3);
}
catch (const std::exception& e) {
std::cout << e.what() << std::endl; // Does not get called
}
Ron
El std::from_chars la función no arroja, solo devuelve un valor de tipo from_chars_result
que es una estructura con dos campos:
struct from_chars_result {
const char* ptr;
std::errc ec;
};
Debe inspeccionar los valores de ptr
y ec
cuando la función devuelve:
#include <iostream>
#include <string>
#include <charconv>
int main()
{
int i3;
std::string_view sv = "abc";
auto result = std::from_chars(sv.data(), sv.data() + sv.size(), i3);
if (result.ec == std::errc::invalid_argument) {
std::cout << "Could not convert.";
}
}
-
Si tiene la intención de asegurarse de que toda la cadena se convirtió en un número, también debe verificar que result.ptr == sv.data() + sv.size() (de lo contrario, no informará ningún error para cadenas como “12qq “), y también debe verificar result.ec==errc{}, no contra std::errc::invalid_argument específicamente, para detectar errores fuera de rango y demás.
– Algún chico
9 de diciembre de 2022 a las 13:54
Bosquecillo
Desafortunadamente, no hay una forma estándar que genere una excepción para usted, pero std::from_chars
tiene un código de valor de retorno que puede usar:
#include <charconv>
#include <stdexcept>
template <class T, class... Args>
void from_chars_throws(const char* first, const char* last, T &t, Args... args) {
std::from_chars_result res = std::from_chars(first, last, t, args... );
// These two exceptions reflect the behavior of std::stoi.
if (res.ec == std::errc::invalid_argument) {
throw std::invalid_argument{"invalid_argument"};
}
else if (res.ec == std::errc::result_out_of_range) {
throw std::out_of_range{"out_of_range"};
}
}
Obviamente puedes crear svtoi
, svtol
de esto, pero la ventaja de “extender” from_chars
es que solo necesita una sola función con plantilla.
-
Si tiene la intención de asegurarse de que toda la cadena se convirtió en un número, también debe verificar que result.ptr == last. De lo contrario, no informará ningún error para cadenas como “12qq”.
– Algún chico
9 dic 2022 a las 13:56
-
@SomeGuy El propósito de esta respuesta es imitar
std::stoi
comportamiento de lanzamiento.std::stoi
no arroja si quedan caracteres restantes, por lo que esta versión no lo hace.– Holt
10 de diciembre de 2022 a las 9:31
-
Aprecio lo que dices, pero sigo pensando que esto es algo que debe ser entendido claramente por cualquiera que lo use. En mi experiencia, casi siempre es deseable convertir toda la cadena pasada, no solo los primeros caracteres de la misma. Desafortunadamente, me he encontrado con bastantes pocos desarrolladores de C++ que entendieron que std::stoi ignora silenciosamente los caracteres finales no analizables, lo que a menudo conduce a errores sutiles. (También me molestó recientemente el hecho de que std::stoi omite los espacios en blanco iniciales, pero std::from_chars no, por lo que su código aún no es un reemplazo directo).
– Algún chico
10 dic 2022 a las 13:01
Sobre la base de las excelentes respuestas de @Ron y @Holt, aquí hay un pequeño envoltorio std::from_chars()
que devuelve un opcional (std::nullopt
cuando la entrada no se puede analizar).
#include <charconv>
#include <optional>
#include <string_view>
std::optional<int> to_int(const std::string_view & input)
{
int out;
const std::from_chars_result result = std::from_chars(input.data(), input.data() + input.size(), out);
if(result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range)
{
return std::nullopt;
}
return out;
}
-
¿Qué pasa cuando
ec
¿Hay algún otro código de falla además de esos dos?–MM
1 de marzo de 2021 a las 4:10
-
@MM, lo mejor que puedo decir de los documentos de CppReferenceesos dos son los solo códigos de error que
std::from_chars()
regresará– s3cur3
1 de marzo de 2021 a las 14:54
-
¿Por qué no simplemente regresar?
out
siec
partidosstd::errc{}
ystd::nullopt
¿de lo contrario? ¿No es esa la alternativa menos detallada? yo tambien pienso esostd::string_view
Los s se alinean con los iteradores y pasarlos por valor es el camino a seguir.– 303
6 de enero de 2022 a las 22:43
-
Si tiene la intención de asegurarse de que toda la cadena se convirtió en un número, también debe verificar que result.ptr == input.data()+input.size(). De lo contrario, no informará ningún error para cadenas como “12qq”.
– Algún chico
9 dic 2022 a las 13:57
Eso es porque
std::from_chars
no tira nada. En su lugar, devuelve un código de error.– Yksisarvinen
17 de junio de 2019 a las 15:34
Al usar std::from_chars, como mencionó @Yksisarvinen, debe verificar el código de error devuelto (result.rc). Además, si tiene la intención de asegurarse de que toda la cadena se convirtió en un número, también debe verificar que result.ptr == sv.data() + sv.size(). De lo contrario, no informará ningún error para cadenas como “12qq”.
– Algún chico
9 dic 2022 a las 13:59