从 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::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::strtoll
和 std::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 位整数。谢谢.
不是唯一可以在浮点文字中但不能在整数中的字符。你也想排除e
和E
。以上是关于从 std::string 解析整数,但如果是浮点数则失败的主要内容,如果未能解决你的问题,请参考以下文章