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