是的std :: Stoi旅馆实际上安全使用?(Is std::stoi actually safe

2019-06-26 10:52发布

我有一个可爱的交谈与某人有关的挫折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。这些条件现在符合了C ++ 11标准stoi投掷std::invalid_argument


这样做的结果关系到我,因为我不想去走一走推荐stoi为字符串的其他方法更好的选择到int的转换,或者使用它自己就好像它的工作,你所希望的方式,如果它不”牛逼捕捉文本为无效的转换。

因此,经过这一切,我有没有出错的地方? 在我看来,我有被抛出此异常的很好的证明。 是我的证明有效,或者std::stoi不能保证抛出异常时给出"abc"

Answer 1:

是否std::stoi扔在输入错误"abcxyz"

是。

我觉得你的混乱可能来自一个事实, strtol永远不会报告错误 ,除了上溢。 它可以报告,不进行转换,但是这绝不称为C标准中的错误条件。

strtol由所有三个C标准类似定义,我就饶了你无聊的细节,但它基本上定义了一个“目标序列”,即对应于实际数量的输入字符串的子串。 以下四种情况是等价的:

  • 主题序列具有预期形式(纯英文:它是一个数字)
  • 主题序列是非空
  • 转换已经发生
  • *endptr != nptr (这仅是有道理的时候endptr不为null)

当存在溢出时,转换仍然说已发生了。

现在,它是很清楚,因为"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是JavaScript和即使如此,你必须强制基地10 parseInt(n, 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>会做你想要什么。 这是特别严格,因为它甚至拒绝周围的空白,不像Python的int()函数。 需要注意的是无效字符和溢出导致相同的异常, 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;
    }
}


文章来源: Is std::stoi actually safe to use?