安全地将 std::string_view 转换为 int(如 stoi 或 atoi)

Phi*_*ZXX 19 c++ string atoi string-view c++17

是否有一种安全的标准方法可以转换std::string_viewint


由于 C++11std::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'
  }
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

所以atoi也不起作用,因为它基于空终止符'\0'(例如sv.substr不能简单地插入/添加一个)。

现在,由于 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
  }
Run Code Online (Sandbox Code Playgroud)

Ron*_*Ron 15

的std :: from_chars功能没有抛出,它只返回类型的值,from_chars_result这两个字段的结构:

struct from_chars_result {
    const char* ptr;
    std::errc ec;
};
Run Code Online (Sandbox Code Playgroud)

您应该检查函数返回的值ptrec时间:

#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.";
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你想确保整个字符串都转换为数字,你还需要检查 result.ptr == sv.data() + sv.size() (否则它不会报告像“12qq”这样的字符串的错误"),并且您还需要检查 result.ec==errc{},而不是专门针对 std::errc::invalid_argument,以捕获超出范围的错误等。 (2认同)

Hol*_*olt 8

不幸的是,没有标准方法可以为您抛出异常,但std::from_chars有一个您可以使用的返回值代码:

#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"};
    }
}

Run Code Online (Sandbox Code Playgroud)

显然,您可以从中创建svtoi, ,但“扩展”的优点是您只需要一个模板化函数。svtolfrom_chars


s3c*_*ur3 7

基于@Ron 和@Holt 的出色答案,这里有一个小包装器std::from_chars(),它返回一个可选值(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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果 `ec` 与 `std::errc{}` 匹配,为什么不简单地返回 `out` ,否则返回 `out` 呢?这不是更简洁的选择吗?我还认为 `std::string_view` 与迭代器一致,按值传递它们是正确的方法。 (4认同)