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

Posted

技术标签:

【中文标题】安全地将 std::string_view 转换为 int(如 stoi 或 atoi)【英文标题】:Safely convert std::string_view to int (like stoi or atoi) 【发布时间】:2019-06-17 15:31:58 【问题描述】:

是否有安全标准方法可以将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 也不起作用,因为它基于空终止符'\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
  

【问题讨论】:

那是因为std::from_chars 不会抛出任何东西。相反,它返回一个错误代码。 【参考方案1】:

std::from_chars 函数不会抛出异常,它只返回一个 from_chars_result 类型的值,这是一个具有两个字段的结构:

struct from_chars_result 
    const char* ptr;
    std::errc ec;
;

您应该在函数返回时检查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.";
    

【讨论】:

【参考方案2】:

不幸的是,没有标准方法可以为您抛出异常,但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";
    


显然你可以从这里创建svtoisvtol,但是“扩展”from_chars的好处是你只需要一个模板化的函数。

【讨论】:

【参考方案3】:

在@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;

【讨论】:

ec 是这两个之外的其他故障代码时呢? @M.M,据the CppReference docs 所知,这两个是std::from_chars() 将返回的唯一错误代码。 如果ec 匹配std::errcstd::nullopt 否则为什么不简单地返回out?这不是更简洁的替代方案吗?我也认为std::string_views 与迭代器一致,通过值传递它们是要走的路。

以上是关于安全地将 std::string_view 转换为 int(如 stoi 或 atoi)的主要内容,如果未能解决你的问题,请参考以下文章

如何将 boost::string_view 转换为 std::string_view?

如何将 std::string_view 转换为双精度?

为啥 std::string_view 比 const char* 快?

真的没有来自 std::string_view 的 std::string 的显式构造函数吗?

c++17比较string_view和string时的歧义

除了 std::string_view 方法之外,std::string_view 比 char* 有啥优势吗?