从 std::string 解析整数,但如果是浮点数则失败

Posted

技术标签:

【中文标题】从 std::string 解析整数,但如果是浮点数则失败【英文标题】:Parse integer from std::string, but fail if float 【发布时间】: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 感谢和抱歉,已更改! 【参考方案1】:

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

try

    number = boost::lexical_cast<int>(your_string);

catch (const boost::bad_lexical_cast& exec)

    // do something on fail

【讨论】:

boost.org/doc/libs/1_61_0/doc/html/boost_lexical_cast/… 这些结果看起来很有希望,谢谢!【参考方案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

以上是关于从 std::string 解析整数,但如果是浮点数则失败的主要内容,如果未能解决你的问题,请参考以下文章

C++ 性能挑战:整数到 std::string 的转换

在 C++ 中将浮点数转换为 std::string

从整数到浮点的转换如何工作?

如何将所有元素合二为一?

归一化整数到/从浮点转换

长整数与 C++ 中的字符串? [关闭]