强制字符串为int函数以消耗整个字符串

Forcing String to int Function to Consume Entire String

本文关键字:字符串 函数 int      更新时间:2023-10-16

给定一个字符串,它应该表示一个数字,我想把它放入一个转换函数,如果整个字符串没有转换,将提供通知。

输入:"12":

  • istringstream::operator>>输出12
  • atoi输出12
  • stoi输出12

对于输入"1X",我想要一个失败响应,但我得到:

  • istringstream::operator>>输出1
  • atoi输出1stoi输出1

输入"X2":

  • istringstream::operator>>输出0并设置错误标志
  • atoi输出0
  • stoi抛出错误
(生活例子)

是否有一种方法可以引起输入"1X"的错误行为?

Edit:在c++17或更高版本中优先使用from_chars。更多信息请看这里:https://topanswers.xyz/cplusplus?q=724#a839


对于给定的string str,有几种方法可以实现这一点,每种方法都有优点和缺点。我在这里写了一个实例:https://ideone.com/LO2Qnq,并在下面讨论:

strtol

如这里所建议的,strtol的out参数可用于获取读取的字符数。strtol实际上返回的是long而不是int,所以在返回时发生了强制转换。

char* size;
const int num = strtol(str.c_str(), &size, 10);
if(distance(str.c_str(), const_cast<const char*>(size)) == str.size()) {
    cout << "strtol: " << num << endl;
} else {
    cout << "strtol: errorn";
}

注意,这里使用str.c_str()来引用同一个字符串。c_str返回作为字符存储的底层数组的指针,如果您使用c++ 11:

c_str()data()执行相同的功能

还请注意,c_str返回的指针在strtoldistance调用之间将有效,除非:

  • string的非const引用传递给任何标准库函数
  • string上调用非const成员函数,不包括operator[]at()front()back()begin()rbegin()end()rend()

如果您违反了上述任何一种情况,您将需要创建i的底层const char*的临时副本并在其上执行测试。

sscanf

sscanf可以使用%zn返回读取的字符数,这可能比做指针比较更直观。如果基础很重要,sscanf可能不是一个好的选择。与strtolstoi不同,sscanf只提供八进制(%o)、十进制(%d)和十六进制(%x)的说明符。

size_t size;
int num;
if(sscanf(str.c_str(), "%d%zn", &num, &size) == 1 && size == str.size()) {
    cout << "sscanf: " << num << endl;
} else {
    cout << "sscanf: errorn";
}

stoi

正如这里所建议的,stoi的输出参数的工作方式类似于sscanf%n,返回读取的字符数。与c++保持一致,这需要一个string,与上面的C实现不同,stoi抛出invalid_argument,如果第一个非空格字符不是当前基数的数字,这不幸意味着与C实现不同,它必须在trycatch块中检查错误。

try {
    size_t size;
    const auto num = stoi(str, &size);
    if(size == str.size()) {
        cout << "stoi: " << num << endl;
    } else {
        throw invalid_argument("invalid stoi argument");
    }
} catch(const invalid_argument& /*e*/) {
    cout << "stoi: errorn";
}

您也可以使用前面提到的std::istringstream,但是要确保它被解析到流的末尾。假设您有一个常量引用,您可以执行如下操作

T parse(const std::string& input) {
    std::istringstream iss(input);
        T result;
        iss >> result;
        if (iss.eof() || iss.tellg() == int(input.size())) {
            return result;
        } else {
            throw std::invalid_argument("Couldn't parse entire string");
    }
}

这种方法的好处是您可以解析任何重载operator>>的内容。注:我不完全确定这个条件是否足够,但根据我的测试,它似乎是足够的。由于某些原因,如果解析到末尾,流将得到一个失败标记。