安全地将 std::string_view 转换为 int(如 stoi 或 atoi)
Safely convert std::string_view to int (like stoi or atoi)
是否有安全标准方法将std::string_view
转换为int
?
因为 C++11 std::string
允许我们使用 stoi
转换为 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'
}
但是stoi
不支持std::string_view
。因此,我们也可以使用 atoi
,但必须非常小心,例如:
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
所以 atoi
也不起作用,因为它基于空终止符 '[=23=]'
(例如 sv.substr
不能简单地 insert/add 一个)。
现在,自 C++17 以来也有 from_chars
,但在提供不良输入时似乎不会抛出:
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
}
std::from_chars 函数不会抛出,它只会 returns 一个 from_chars_result
类型的值,它是一个有两个字段的结构:
struct from_chars_result {
const char* ptr;
std::errc ec;
};
当函数 returns:
时,您应该检查 ptr
和 ec
的值
#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.";
}
}
不幸的是,没有标准方法可以为您抛出异常,但 std::from_chars
有一个 return 值代码可供您使用:
#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"};
}
}
显然你可以从中创建svtoi
、svtol
,但是"extending"from_chars
的优点是你只需要一个模板函数。
基于@Ron 和@Holt 的优秀答案,这里有一个围绕 std::from_chars()
的小包装器,returns 一个可选的(std::nullopt
当输入无法解析时)。
#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;
}
是否有安全标准方法将std::string_view
转换为int
?
因为 C++11 std::string
允许我们使用 stoi
转换为 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'
}
但是stoi
不支持std::string_view
。因此,我们也可以使用 atoi
,但必须非常小心,例如:
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
所以 atoi
也不起作用,因为它基于空终止符 '[=23=]'
(例如 sv.substr
不能简单地 insert/add 一个)。
现在,自 C++17 以来也有 from_chars
,但在提供不良输入时似乎不会抛出:
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
}
std::from_chars 函数不会抛出,它只会 returns 一个 from_chars_result
类型的值,它是一个有两个字段的结构:
struct from_chars_result {
const char* ptr;
std::errc ec;
};
当函数 returns:
时,您应该检查ptr
和 ec
的值
#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.";
}
}
不幸的是,没有标准方法可以为您抛出异常,但 std::from_chars
有一个 return 值代码可供您使用:
#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"};
}
}
显然你可以从中创建svtoi
、svtol
,但是"extending"from_chars
的优点是你只需要一个模板函数。
基于@Ron 和@Holt 的优秀答案,这里有一个围绕 std::from_chars()
的小包装器,returns 一个可选的(std::nullopt
当输入无法解析时)。
#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;
}