std::stoi有更严格的版本吗?

Is there a more stringent version of std::stoi?

我刚刚发现(令我大吃一惊)以下输入 不会 导致 std::stoi 引发异常:

3.14
3.14helloworld

违反了最小意外原则 - 因为其中 none 是有效的格式整数值。

注意,也许更令人惊讶的是 3.8 被转换为值 3

是否有更严格的 std::stoi 版本,它会在输入确实不是有效整数时抛出?还是我必须自己动手?

顺便说一句,为什么 C++ 标准库会这样实现 std::stoi?这个函数的唯一实际用途是拼命尝试从随机垃圾输入中获取一些整数值——这似乎不是一个非常有用的函数。

这是我的解决方法。

static int convertToInt(const std::string& value)
{
    std::size_t index;
    int converted_value{std::stoi(value, &index)};

    if(index != value.size())
    {
        throw std::runtime_error("Bad format input");
    }

    return converted_value;
}

您问题的答案:

Is there a more stringent version of std::stoi?

是:不,不在标准库中。

std::stoi,如 here 所述,其行为与 CPP 参考中解释的一样:

Discards any whitespace characters (as identified by calling std::isspace) until the first non-whitespace character is found, then takes as many characters as possible to form a valid base-n (where n=base) integer number representation and converts them to an integer value. The valid integer value consists of the following parts: . . . . .

如果您想要一个更强大的 std::stoi 版本来满足您的特殊需求,您确实需要编写自己的函数。

有那么多潜在的实现,但没有一个“正确”的解决方案。这取决于您的需求和编程风格。

我只是向您展示(众多可能之一)示例解决方案:

#include <iostream>
#include <string>
#include <utility>
#include <regex>

// Some example. Many many other different soultions possible
std::pair<int, bool> stoiSpecial(const std::string s) {

    int result{};
    bool validArgument{};

    if (std::regex_match(s, std::regex("[+-]?[0-9]+"))) {
        try {
            result = stoi(s);
            validArgument = true;
        }
        catch (...) {};
    }
    return {result, validArgument };
}

// Some test code
int main() {
    
    std::string valueAsString{};
    std::getline(std::cin,valueAsString);

    if (const auto& [result, validArgument] = stoiSpecial(valueAsString); validArgument)
        std::cout << result << '\n';
    else
        std::cerr << "\n\n*** Error: Invalid Argument\n\n";
}

Is there a more stringent version of std::stoi which will throw when an input really is not a valid integer? Or do I have to roll my own?

您将不得不自己动手,因为您的需求与 一种一致的、不足为奇的方式发生冲突,其中 all“字符串到整数”功能在C 和 C++ 均已定义.

首先,您必须提出对“有效整数”的定义。您是否接受前导 0(八进制)、前导 0x(十六进制)和/或前导 0b(二进制)?您接受前导空格吗?

如果您对两者都满意,那么您的解决方法就足够了。否则,您必须检查字符串的第一个字符是 isdigit 还是 non-null.


I just discovered (much to my surprise) that the following inputs do not cause std::stoi to throw an exception:

阅读 a good reference 任何您不 非常 熟悉的功能,然后再使用它是一个相当基本的要求。

该参考文献非常清楚地指出,在跳过任何前导空格后,它将采用“尽可能多的字符”来形成“有效的 [...] 整数表示形式”,并且第二个参数“将接收第一个未转换字符的地址。

Violating the principle of least surprise - since none of these are valid format integer values.

Note, perhaps even more surprisingly 3.8 is converted to the value 3.

Is there a more stringent version of std::stoi which will throw when an input really is not a valid integer? Or do I have to roll my own?

这里有一个严重的问题:您已经做出了假设,却没有费心用参考来验证它们,现在却深挖自己更了解的东西。您在内部观察到的行为不仅与 C++ 的所有 istream operator>>std::sto* 系列以及 C 的 *scanfstrto*ato* 系列一致。这也是 Java 的 Scanner.nextInt()、C# 的 int.TryParse、Perl 的 int 以及其他十几种语言的类似函数的工作方式。

(顺便说一下,对于各种 floating-point 解析函数也是如此。)


Why is std::stoi implemented this way?

因为这个一般use-case的最有效实现。

The only practical use this function has is to desperately try and obtain some integer value from random garbage input - which doesn't seem like a very useful function.

考虑:

4;3.14;16

那显然不是“随机垃圾输入”,而是semicolon-separated数据——经常遇到的东西,你会同意的。

如果“读取 int”会在 non-digit 输入处抛出异常,就像您建议的那样,我们会考虑至少抛出两个异常来解析这一 non-exceptional 行输入。或者,我们必须两次 传递该输入,首先是为了找到分号/行结束(并且必须写入输入字符串或设置几个临时变量),然后第二次解析。那将是非常低效的。