我有一些输入要由输入文件流读取(例如):
-365.269511 -0.356123 -Inf 0.000000
当我使用std::ifstream mystream;
从文件中读取某些
double d1 = -1, d2 = -1, d3 = -1, d4 = -1;
(假设mystream
已经打开且文件有效),
mystream >> d1 >> d2 >> d3 >> d4;
mystream
处于失败状态。我希望
std::cout << d1 << " " << d2 << " " << d3 << " " << d4 << std::endl;
输出
-365.269511 -0.356123 -1 -1
。我希望它能输出-365.269511 -0.356123 -Inf 0
。
这组数据是使用C ++流输出的。为什么我不能进行反向处理(在我的输出中读取)?我怎样才能获得我寻求的功能?
来自MooingDuck:
#include <iostream>
#include <limits>
using namespace std;
int main()
{
double myd = std::numeric_limits<double>::infinity();
cout << myd << '\n';
cin >> myd;
cout << cin.good() << ":" << myd << endl;
return 0;
}
输入:inf
输出:
inf
0:inf
与此问题相关的还有NaN
解析,即使我没有举例说明。
我在接受的答案中添加了关于ideone的完整解决方案。它还包括削减“Inf”和“nan”,这些关键字可能会来自其他程序,例如MatLab。
答案 0 :(得分:13)
更新提供了一个简单的测试用例,显示Boost Spirit能够处理此区域中的各种特殊值。见下文:提升精神(FTW)。
我能够找到的这一领域唯一的规范性信息见C99标准的第7.19.6.1/7.19.6.2节。
可悲的是,最新的C ++标准文档(n3337.pdf)的相应部分似乎并没有以相同的方式指定对infinity
,inf
和/ NaN
的支持。 (也许我错过了一个引用C99 / C11规范的脚注?)
2000年,Apache libstdcxx收到a bug report陈述
num_get<>
方面的do_get()
成员未能考虑特殊字符串[-]inf[inity]
和[-]nan
。 facet在遇到此类字符串时会报告错误。有关允许的字符串列表,请参见C99的7.19.6.1和7.19.6.2。
然而,随后的讨论得出结论(至少在名为locale
- s 的情况下),实现解析特殊值实际上是非法的:
查找表中的字符是“0123456789abcdefABCDEF + - ”。 图书馆问题221将修改为“0123456789abcdefxABCDEFX + - ”。 查询表中不存在“N”,因此num_get&lt;&gt; :: do_get()的第2阶段 不允许读取字符序列“NaN”。
securecoding.cert.org明确指出以下“合规代码”必需,以避免解析 infinity 或 NaN 。这意味着,某些实现实际上支持这一点 - 假设作者曾测试过已发布的代码。
#include <cmath>
float currentBalance; /* User's cash balance */
void doDeposit() {
float val;
std::cin >> val;
if (std::isinf(val)) {
// handle infinity error
}
if (std::isnan(val)) {
// handle NaN error
}
if (val >= MaxValue - currentBalance) {
// Handle range error
}
currentBalance += val;
}
以下简单示例具有所需的输出:
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main()
{
const std::string input = "3.14 -inf +inf NaN -NaN +NaN 42";
std::vector<double> data;
std::string::const_iterator f(input.begin()), l(input.end());
bool ok = qi::parse(f,l,qi::double_ % ' ',data);
for(auto d : data)
std::cout << d << '\n';
}
输出:
3.14
-inf
inf
nan
-nan
nan
42
我倾向于说C99指定* printf / * scanf包含 infinity 和 NaN 的行为。遗憾的是,在存在命名语言环境的情况下,C ++ 11似乎没有指定它(甚至禁止它)。
答案 1 :(得分:9)
使用如下签名编写函数:
std::istream & ReadDouble(std::istream & is, double & d);
在里面,你:
operator>>
std::stod
,boost::lexical_cast
等...... 答案 2 :(得分:9)
编辑:为避免在double周围使用包装器结构,我将istream
包含在包装器类中。
不幸的是,我无法弄清楚如何通过为double
添加另一种输入法来避免产生歧义。对于下面的实现,我在istream
周围创建了一个包装器结构,包装器类实现了输入方法。输入法确定否定性,然后尝试提取双重性。如果失败,则启动解析。
修改:感谢您让我更好地检查错误情况。
struct double_istream {
std::istream ∈
double_istream (std::istream &i) : in(i) {}
double_istream & parse_on_fail (double &x, bool neg);
double_istream & operator >> (double &x) {
bool neg = false;
char c;
if (!in.good()) return *this;
while (isspace(c = in.peek())) in.get();
if (c == '-') { neg = true; }
in >> x;
if (! in.fail()) return *this;
return parse_on_fail(x, neg);
}
};
解析例程实现起来比我原先想象的要复杂一点,但我想避免尝试putback
整个字符串。
double_istream &
double_istream::parse_on_fail (double &x, bool neg) {
const char *exp[] = { "", "inf", "NaN" };
const char *e = exp[0];
int l = 0;
char inf[4];
char *c = inf;
if (neg) *c++ = '-';
in.clear();
if (!(in >> *c).good()) return *this;
switch (*c) {
case 'i': e = exp[l=1]; break;
case 'N': e = exp[l=2]; break;
}
while (*c == *e) {
if ((e-exp[l]) == 2) break;
++e; if (!(in >> *++c).good()) break;
}
if (in.good() && *c == *e) {
switch (l) {
case 1: x = std::numeric_limits<double>::infinity(); break;
case 2: x = std::numeric_limits<double>::quiet_NaN(); break;
}
if (neg) x = -x;
return *this;
} else if (!in.good()) {
if (!in.fail()) return *this;
in.clear(); --c;
}
do { in.putback(*c); } while (c-- != inf);
in.setstate(std::ios_base::failbit);
return *this;
}
此例程对默认double
输入的行为的一个区别是,如果输入是-
,则不会消耗"-inp"
字符。如果失败,"-inp"
仍会在double_istream
的流中,但对于常规istream
,只有"inp"
会留在流中。
std::istringstream iss("1.0 -NaN inf -inf NaN 1.2");
double_istream in(iss);
double u, v, w, x, y, z;
in >> u >> v >> w >> x >> y >> z;
std::cout << u << " " << v << " " << w << " "
<< x << " " << y << " " << z << std::endl;
我系统上面代码段的输出是:
1 nan inf -inf nan 1.2
编辑:像助手类一样添加“iomanip”。当double_imanip
对象在>>
链中出现多次时,它将像切换一样。
struct double_imanip {
mutable std::istream *in;
const double_imanip & operator >> (double &x) const {
double_istream(*in) >> x;
return *this;
}
std::istream & operator >> (const double_imanip &) const {
return *in;
}
};
const double_imanip &
operator >> (std::istream &in, const double_imanip &dm) {
dm.in = ∈
return dm;
}
然后使用以下代码进行尝试:
std::istringstream iss("1.0 -NaN inf -inf NaN 1.2 inf");
double u, v, w, x, y, z, fail_double;
std::string fail_string;
iss >> double_imanip()
>> u >> v >> w >> x >> y >> z
>> double_imanip()
>> fail_double;
std::cout << u << " " << v << " " << w << " "
<< x << " " << y << " " << z << std::endl;
if (iss.fail()) {
iss.clear();
iss >> fail_string;
std::cout << fail_string << std::endl;
} else {
std::cout << "TEST FAILED" << std::endl;
}
以上的输出是:
1 nan inf -inf nan 1.2
inf
从Drise编辑:我做了一些编辑,接受了最初未包含的Inf和nan等变体。我还把它变成了一个编译演示,可以在http://ideone.com/qIFVo查看。
答案 3 :(得分:3)
只需将变量读入字符串并解析它们即可。你不能将字符串放入双变量并期望它们像字符串一样输出,因为如果它有效,则不需要字符串。
像:
string text;
double d;
while(cin >> text)
{
if(text == "Inf") //you could also add it with negative infinity
{
d = std::numeric_limits<double>::infinity();
}
else
{
d = atof(text.c_str());
}
}
答案 4 :(得分:0)
尽管这个问题已经很老了,但我想提供最适合我的目的的解决方案。如果您考虑使用Boost,但Boost Spirit似乎有些过分,可以尝试使用boost::math::nonfinite_num_put
和boost::math::nonfinite_num_get
语言环境方面Swift version,如下所示:
#include <boost/math/special_functions/nonfinite_num_facets.hpp>
#include <iostream>
#include <limits>
#include <sstream>
int main()
{
std::locale default_locale;
std::locale tmp_locale(default_locale,
new boost::math::nonfinite_num_put<char>());
std::locale upgraded_locale(default_locale,
new boost::math::nonfinite_num_get<char>());
double inf = std::numeric_limits<double>::infinity();
std::stringstream out_s;
out_s.imbue(upgraded_locale);
out_s << inf;
std::cout << out_s.str() << std::endl;
std::stringstream in_s(out_s.str());
in_s.imbue(upgraded_locale);
double check_inf;
in_s >> check_inf;
std::cout << (inf == check_inf ? "nice" : "not nice") << std::endl;
return 0;
}
输出:
inf
nice
答案 5 :(得分:-1)
您必须编写自定义提取功能,因为您的具体实现显然无法正确处理它们。