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

问题描述 投票:0回答:4

是否有一种安全标准方法将

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
  }
c++ string c++17 atoi string-view
4个回答
37
投票

std::from_chars函数不会抛出异常,它只返回

from_chars_result
类型的值,这是一个具有两个字段的结构:

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

当函数返回时,您应该检查

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

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

显然你可以由此创建

svtoi
svtol
,但“扩展”
from_chars
的优点是你只需要一个模板化函数。


6
投票

基于@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;
}

0
投票

不确定您在担心什么。 不管怎样,我给你做了一个测试。 对于适合小字符串优化的任何 int 的字符串长度,将 string_view 转换为字符串所需的时间:1.0498 纳秒。 即使对于不适合的较长字符串,也需要:8.748 纳秒。

所以只需将其转换为字符串,然后可能在内联函数中将其转换为 int ,这样你就有了自己的“安全标准”方式:)

© www.soinside.com 2019 - 2024. All rights reserved.