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

我和一个关于std::stoi的垮台的人进行了一次可爱的交谈。 说穿了,它内部使用std::strtol ,并抛出,如果报告错误。 据他们说,虽然std::strtol不应该为"abcxyz"input"abcxyz" ,导致stoi不会抛出std::invalid_argument

首先,这里有两个程序在GCC上testing了这些案例的行为:
与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的行为。 现在怎么样? 我在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开头的nptr将被存储在endptr ,指针传入。这似乎与testing一致。 另外,应该返回0,如下所述:

§7.22.1.4

 If no conversion could be performed, zero is returned. 

前面的引用说,不会执行转换,所以它必须返回0.这些条件现在符合stoi std::invalid_argument的C ++ 11标准。


这个结果对我来说很重要,因为我不想推荐stoi作为其他string到int转换方法的更好的替代方法,或者如果它按照您期望的方式工作,将文本视为无效的转换。

所以,在这之后,我在某个地方出了问题吗? 在我看来,我有这个例外被抛出的良好证据。 我的certificate是有效的,或者是std::stoi在给定"abc"时不保证抛出exception?

std::stoi在input"abcxyz"上抛出一个错误吗?

是。

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

strtol被所有三个C标准类似地定义,我会省去你无聊的细节,但它基本上定义了一个“主题序列”,它是与实际编号对应的inputstring的子string。 以下四个条件是等价的:

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

当发生溢出时,转换仍然被认为发生了。

现在很清楚,因为"abcxyz"不包含数字,string"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; } 

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


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

不,你说的那个人是正确的,当她说std::stoi比执行完整的检查errno == 0 && end != start && *end=='\0'std::strtol之后更加宽松时,因为std::stoi静静地std::stoi了string中第一个非数字字符开始的所有字符。

事实上,我的头顶上只有原生转换行为类似于std::stoi是Javascript,即使这样你也必须用parseInt(n, 10)强制使用parseInt(n, 10)来避免hex数的特殊情况:

 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>将做你想要的。 这是非常严格的,因为它甚至拒绝周围的空白,不像Python的int()函数。 请注意,无效字符和溢出会导致相同的exception, 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; } }