std :: stoi实际上是否可以安全使用?

时间:2012-07-22 09:08:18

标签: c++ standards language-lawyer

我与某人就std::stoi的垮台进行了愉快的交谈。说穿了,它在内部使用std::strtol,如果报告错误则抛出。但是,根据他们的说法,std::strtol不应为"abcxyz"的输入报告错误,导致stoi不投掷std::invalid_argument

首先,这里有两个在GCC上测试过的关于这些案件行为的程序:
strtol
stoi

它们都显示"123"成功,"abc"失败。


我查看标准以获取更多信息:

§21.5

Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that  
no conversion could be performed. Throws out_of_range if the converted value is  
outside the range of representable values for the return type.

总结了依赖strtol的行为。那么strtol呢?我在C11草案中找到了这个:

§7.22.1.4

If the subject sequence is empty or does not have the expected form, no  
conversion is performed; the value of nptr is stored in the object  
pointed to by endptr, provided that endptr is not a null pointer.

考虑到传递"abc"的情况,C标准规定指向字符串开头的nptr将存储在endptr中,指针传入。这似乎与测试一致。此外,应返回0,如下所述:

§7.22.1.4

If no conversion could be performed, zero is returned.

之前的参考文献表示不会执行转换,因此必须返回0.这些条件现在符合stoi抛出std::invalid_argument的C ++ 11标准。


这对我很重要,因为我不想四处推荐stoi作为其他字符串转换为int的更好的替代方法,或者自己使用它就好像它的工作方式一样' d期望,如果它没有将文本视为无效转换。

所以在这之后,我在某个地方出错了吗?在我看来,我有很好的证据证明这个例外被抛出。我的证明是否有效,或者std::stoi"abc"给出{{1}}时不能保证抛出该异常?

1 个答案:

答案 0 :(得分:72)

std::stoi是否会在输入"abcxyz"上抛出错误?

我认为您的困惑可能来自strtol从不报告错误这一事实,除了溢出。它可以报告没有执行转换,但这在C标准中从未被称为错误条件。

strtol的定义类似于所有三个C标准,我将为您节省无聊的细节,但它基本上定义了一个“主题序列”,它是与实际数字对应的输入字符串的子串。以下四个条件是等效的:

  • 主题序列具有预期的形式(用简单的英语:它是一个数字)
  • 主题序列非空
  • 已发生转化
  • *endptr != nptr(仅在endptr为非空时才有意义)

当出现溢出时,仍然认为转换已经发生。

现在,很明显因为"abcxyz"不包含数字,字符串"abcxyz"的主题序列必须为空,因此不能执行转换。以下C90 / C99 / C11程序将通过实验确认:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *nptr = "abcxyz", *endptr[1];
    strtol(nptr, endptr, 0);
    if (*endptr == nptr)
        printf("No conversion could be performed.\n");
    return 0;
}

这意味着当给定输入std::stoi而没有可选的基本参数时,任何符合invalid_argument 的实现必须抛出"abcxyz"


这是否意味着std::stoi有令人满意的错误检查?

没有。如果std::stoi表示errno == 0 && end != start && *end=='\0'std::strtol之后执行完整检查std::stoi更宽松,那么您正在与之交谈的人是正确的,因为std::stoi会默默地删除所有字符字符串中的第一个非数字字符。

事实上,在我的脑海中,唯一一种其原生转换行为与parseInt(n, 10)类似的语言是Javascript,即便如此,你必须强制基数为10 input | std::atoi std::stoi Javascript full check ===========+============================================================= hello | 0 error error(NaN) error 0xygen | 0 0 error(NaN) error 0x42 | 0 0 66 error 42x0 | 42 42 42 error 42 | 42 42 42 42 -----------+------------------------------------------------------------- languages | Perl, Ruby, Javascript Javascript C#, Java, | PHP, C... (base 10) Python... 以避免十六进制的特殊情况数:

boost::lexical_cast<int>

注意:在处理空格和冗余+符号时,语言之间也存在差异。


好的,所以我想要完整的错误检查,我应该使用什么?

我不知道有任何内置函数执行此操作,但int()将执行您想要的操作。它特别严格,因为它甚至拒绝周围的空格,与Python的boost::bad_lexical_cast函数不同。请注意,无效字符和溢出会导致相同的异常#include <boost/lexical_cast.hpp> int main() { std::string s = "42"; try { int n = boost::lexical_cast<int>(s); std::cout << "n = " << n << std::endl; } catch (boost::bad_lexical_cast) { std::cout << "conversion failed" << std::endl; } }

{{1}}