【问题标题】:Forcing String to int Function to Consume Entire String强制字符串为 int 函数以消耗整个字符串
【发布时间】:2016-01-04 14:58:12
【问题描述】:

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

输入:"12"

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

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

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

对于输入"X2"

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

[Live Example]

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

【问题讨论】:

  • @deepmax 是的,出于最小示例的目的,我没有包含它,但在我的本地测试代码中,我在循环的顶部执行此操作:cout << "\tinput string: " << i << (all_of(i, i + strlen(i), bind(isdigit, placeholders::_1)) ? " is good\n" : " is bad\n"); 但是,即使虽然我可以检查这种方式我不想。所有其他函数也必须逐步遍历每个字符,我希望有一种方法来利用他们已经知道的内容。
  • @deepmax 你错了,这不是重复的,这些答案不是地址验证整个字符串已被读取。
  • 我同意这不是重复的。它提出了一个不同的问题,链接的答案没有回答这个问题。 @deepmax 你应该取消标记它。
  • 不管怎样,答案是:int pos; int n = stoi(mystring, &pos); if (pos != mystring.length()) 它不是数字。

标签: c++ string atoi string-conversion extraction-operator


【解决方案1】:

编辑: 或更高版本from_chars 是首选。更多信息请看这里:https://topanswers.xyz/cplusplus?q=724#a839


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

strtol

如建议here strtol 的输出参数可用于获取读取的字符数。 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: error\n";
}

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

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

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

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

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

sscanf

sscanf 可以使用%zn 返回读取的字符数,这可能比进行指针比较更直观。如果 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

正如建议的 here 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: error\n";
}

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我给了你一个 +1,因为对于需要进一步使用istringstream 并可以合并你的解决方案的人来说,这可能是一种非常有用的转换方式,但在一般情况下我认为应该使用my answer 中更轻量级的转换之一。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 1970-01-01
    • 2011-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多