强制 String to int 函数消耗整个字符串

Forcing String to int Function to Consume Entire String

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

输入:"12":

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

输入"X2":

[Live Example]

有没有办法在输入时引发错误行为"1X"

编辑: or later from_chars is preferred. See here for more: https://topanswers.xyz/cplusplus?q=724#a839


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

strtol

如建议的那样 here strtol 的输出参数可用于获取读取的字符数。 strtol 实际上 return 是 long 而不是 int 所以在 return.

上发生了转换
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 Returns 指向用作字符存储的底层数组的指针,如果您有 C++11,则不是临时数组:

c_str() and data() perform the same function

另请注意,由 c_str 编辑的指针 return 将在 strtoldistance 调用之间有效,除非:

  • Passing a non-const reference to the string to any standard library function
  • Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend()

如果您违反了其中任何一种情况,您将需要临时复制 i 的基础 const char* 并对其进行测试。

sscanf

sscanf可以用%zn到return读取的字符数,这可能比做指针比较更直观。如果 base 很重要,sscanf 可能不是一个好的选择。与支持基数 2 - 36 的 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: error\n";
}

stoi

如建议 stoi's output parameter works like sscanf's %n returning the number of characters read. In keeping with C++ this takes a string and unlike the C implementations above stoi throws an 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";
}

或者您可以像您提到的那样使用 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>> 的内容。注意:我不完全确定条件是否足够,但根据我的测试似乎是这样。出于某种原因,如果流解析到末尾,它将获得失败标记。