【问题标题】:Parse integer from std::string, but fail if float从 std::string 解析整数,但如果是浮点数则失败
【发布时间】:2016-08-30 12:01:39
【问题描述】:

在 C++ 和 C 中有多种方法可以将字符串转换为整数,但我还没有找到解析浮点数失败的转换方法。

const float fnum = std::stof("1.5");
std::cout << fnum << std::endl; // prints "1.5", all okay

const int inum = std::stoi("1.5");
std::cout << inum << std::endl; // prints "1", but wrong!

我需要这个来分析列类型的 CSV 文件。如果一列中的所有字段都是整数,则将该列存储为 std::vector,如果是浮点数,则存储为 std::vector,否则将其存储为字符串。

唯一看起来很有希望的方法是:

std::string num = "1.5";
char *end = nullptr;

const long lnum = strtol(num.data(), &end, 10);
if (end != &*num.end()) {
    std::cout << "Float? " << l << " / " << num << std::endl;
} else {
    std::cout << "Integer! " << l << " / " << num << std::endl;
}

这可行,但很丑陋。有没有 C++ 方式来解决这个问题?

【问题讨论】:

  • std::stoi 有另一个参数可以像strtol 的结束参数一样使用。如果您发现它“丑陋”,那么如何搜索字符串以确保 all 字符是 digits
  • @dommynik - 你确定const int fnum 吗?不是const float fnum吗?
  • 除了 Joachim 所说的之外,您还可以使用 stringstream 并在转换调用后检查流是否为空。
  • 没有真正好的方法。无论哪种方式 a) 使用字符串解析。或者 b) 你通过 boost 使用 lexical_cast (这应该抛出有点难看的异常)或者 c) 你之后将它转换回字符串并查看字符串是否相同。啊,是的,还有字符串流。
  • @max66 感谢和抱歉,已更改!

标签: c++ c++11


【解决方案1】:

你可以使用 boost lexical_cast。如果强制转换失败,它会抛出异常

try
{
    number = boost::lexical_cast<int>(your_string);
}
catch (const boost::bad_lexical_cast& exec)
{
    // do something on fail
}

【讨论】:

【解决方案2】:

您应该反复检查数字是否将 1) 解析为整数,2) 解析为浮点数,最后 3) 两者都不解析。但是“解析”应该意味着整个字符串都被消耗掉了。

试试这样的:

#include <sstream>
#include <string>

bool TryAsInt(const std::string & s, long long int & out)
{
    std::istringstream iss(s);
    return (iss >> out >> std::ws) && (iss.get() == EOF);
}

浮点数也是如此。

如果您不喜欢为此使用 iostreams,您也可以使用 std::strtollstd::strtod 等。这也允许您控制整数基数。例如:

#include <cerrno>
#include <cstdlib>
#include <string>

bool TryAsInt(const std::string & s, long long int & out)
{
    char * e;
    errno = 0;

    out = std::strtoll(s.data(), &e, 0);
    return errno == 0 && s.data() + s.size() == e;
}

那么您仍然必须将其与检查所有字段的逻辑结合起来。

例如:

std::vector<string> raw_fields;

long long int n;
double x;

if (std::all_of(raw_fields.begin(), raw_fields.end(),
    [&n](const string & s) { return TryAsInt(s, n); })
{
    // integer case
}
else if (std::all_of(raw_fields.begin(), raw_fields.end(),
    [&x](const string & s) { return TryAsFloat(s, x); })
{
    // floating point case
}
else
{
    // just use raw_fields as-is
}

【讨论】:

  • 谢谢,我会尝试一下 - 但我必须运行一些计时基准来比较 boost、stringstreams 和 stoi。
  • @dommynik:如果您需要性能,那么对输入执行原始词法分析而不是进行所有冗余解析会更有效。这并不完全是微不足道的,而是可行的。
【解决方案3】:

使用普通的 std::stoi ,但一定要检查字符串是否被完全消耗。例如:

static bool isIntValue(const std::string& string, int32_t& intValue)
{
    try
    {
        size_t lastChar;
        intValue = std::stoi(string, &lastChar);
        return lastChar == string.size();
    }
    catch (...)
    {
        return false;
    }
}

此代码的执行速度比 std::istringstream 解决方案快得多。

【讨论】:

    【解决方案4】:

    如果您需要更多类型检查,请查看 boost Spirit X3。 例如,您可以这样做:

    #include <boost/spirit/home/x3.hpp>
    #include <iostream>
    #include <string>
    
    template<typename T, typename RuleType>
    bool is_type(std::string const& str, RuleType const& rule, T& val)
    {
        using namespace boost::spirit::x3;
    
        auto beg = std::begin(str);
        auto end = std::end(str);
    
        auto ret = parse(beg,end, rule, val);
        return ret && (beg==end);
    }
    
    int main(int argc, char** argv)
    {
        std::string s1="1.0";
        float v;
        std::cout << s1 << " is int ?   : " << is_type(s1, boost::spirit::x3::int_, v) << "\n";
        std::cout << s1 << " is float ? : " << is_type(s1, boost::spirit::x3::float_, v) << "\n";
    
    
    
        return 0;
    }
    

    注意:您可以指定更严格的解析(看这里:https://github.com/djowel/spirit_x3/tree/master/include/boost/spirit/home/x3/numeric

    【讨论】:

      【解决方案5】:

      您希望对包含十进制数字的数字失败。您可以测试点,也可以测试十进制数字。

      if (num.find('.') != string::npos || num.find(',') != string::npos)
         cout << "Number is not integer" << endl;
      

      第二个选项:

      double fnum = std::stod(num);
      if (fnum != (long)fnum)
         cout << "Number is not integer" << endl;
      

      您不必担心浮点舍入错误,因为每个整数都可以精确地表示为 2^53 以内的双精度数。

      【讨论】:

      • 如果 int 是 32 位并且也是浮点数,那么浮点数不能准确地表示所有 int 值。
      • 是的,我刚刚意识到浮点数只包含 24 位整数。谢谢
      • . 不是唯一可以在浮点文字中但不能在整数中的字符。你也想排除eE
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-27
      • 1970-01-01
      • 2014-06-17
      • 1970-01-01
      • 2016-02-09
      • 1970-01-01
      • 2016-01-21
      相关资源
      最近更新 更多