6

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

对于输入"12"::

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

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

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

对于输入"X2"

  • istringstream::operator>>输出 0 并设置错误标志
  • atoi输出 0
  • stoi抛出错误

[现场示例]

有没有办法在 input 上引发错误行为"1X"

4

2 回答 2

3

编辑:或更高版本from_chars中是首选。请参阅此处了解更多信息:https ://topanswers.xyz/cplusplus?q=724#a839


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

strtol

正如这里 strtol所建议的,可以使用 out-parameter 来获取读取的字符数。strtol实际上返回 a longnot anint所以在返回时会发生强制转换。

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: error\n";
}

请注意,这用于str.c_str()引用相同的字符串。c_str如果您有 C++11,则返回指向用作字符存储的底层数组的指针,而不是临时的:

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

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

  • 将非const引用传递string给任何标准库函数
  • 在、、、、、、和上调用非const成员函数stringoperator[]at()front()back()begin()rbegin()end()rend()

如果您违反其中任何一种情况,您需要制作i' 底层证券的临时副本const char*并对其进行测试。

sscanf

sscanf可以%zn用来返回读取的字符数,这可能比进行指针比较更直观。如果基础很重要,sscanf可能不是一个好的选择。与支持基数 2 - 36 不同,strtol它只为八进制 ( )、十进制 ( ) 和十六进制 ( ) 提供说明符。stoisscanf%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: error\n";
}

stoi

正如这里 stoi所建议的,输出参数的工作方式类似于返回读取sscanf%n字符数。与 C++ 保持一致,这需要 astring并且与上面的 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: error\n";
}
于 2015-10-07T16:39:10.817 回答
1

或者,您可以使用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>>。注意:我不完全确定条件是否足够,但根据我的测试,它似乎是。出于某种原因,如果流解析到最后,它会得到一个失败标记。

于 2016-01-31T23:54:37.990 回答