问题描述
我试图了解 C++11 的正确行为应该是什么 std::get_time() 当输入数据比格式预期的“短”时 细绳。例如,下面的程序应该打印什么:
#include <ctime>
#include <iomanip>
#include <sstream>
#include <iostream>
int main (int argc,char* argv[])
{
using namespace std;
tm t {};
istringstream is ("2016");
is >> get_time (&t,"%Y %d");
cout << "eof: " << is.eof () << endl
<< "fail: " << is.fail () << endl;
}
注意 get_time() 行为被描述为 std::time_get<CharT,InputIt>::get()。 基于后者(见 1c 段),我希望 eofbit 和 failbit 被设置,所以要打印的程序:
eof: 1
fail: 1
然而,对于所有主要的标准 C++ 库实现(libstdc++ 10.2.1、libc++ 11.0.0 和 MSVC 16.8)它打印:
eof: 1
fail: 0
有趣的是,对于 16.8 之前的 MSVC,它会打印:
eof: 1
fail: 1
但是提交 "std::get_time should not fail when format is longer than the stream" 表明这是故意修复的。
有人可以澄清所提到的标准库是否(以及为什么)行为正确,如果是这种情况,应该如何检测格式字符串未完全使用。
解决方法
我无法以 100% 的准确度进行解释,但我可以尝试解释为什么该函数的行为与观察到的一样。
我怀疑 eofbit
未在您的情况下设置,即 1c,因为情况 1b 优先:
b) 出现解析错误 (err != std::ios_base::goodbit
)
根据 The eofbit 的 https://en.cppreference.com/w/cpp/io/ios_base/iostate 部分,eof 位被设置的情况之一是当
std::get_time
I/O 操作符和任何 std::time_get
解析函数:time_get::get
、time_get::get_time
、time_get::get_date
等,如果在处理解析预期日期/时间值所需的最后一个字符之前到达流。
failbit
的同一来源说:
时间输入操纵器 std::get_time
(技术上,它调用的 time_get::get
),如果输入不能根据给定的格式字符串明确解析为时间值。
所以我的猜测是,当输入为 2000 时,get
尝试使用 operator>>(std::string&)
读取它,命中 eof 条件并设置 eofbit
。这满足条件1b,所以不能应用条件1c。
如果函数需要年份并且输入短于 4 位,例如200
,或者在年份之后包含空格2000
,或者包含超过4位数字20001
,函数返回failbit
。但是,如果输入是一个以 0 开头的 4 位数字,例如0005
,函数返回 eofbit
== 1,failbit == 0
。这符合 %Y
格式说明符的规范:
将全年解析为 4 位十进制数,允许但不要求前导零
所以我希望这可以解释为什么有时不考虑条件 1c。我们可以通过测试 good()
成员函数来检测格式字符串没有以通常的方式完全使用。我相信告诉函数返回 failbit
== 1 或 0 之间的区别没有什么实际意义。我也认为这里的标准不精确,但如果我们假设用户对 good()
的值感兴趣,那么这种缺乏精确性与实际无关。
在您考虑的情况下,failbit
的值也可能是实现定义的:实现可以尝试读取恰好 4 个字符以满足 %Y
格式说明符,在这种情况下eofbit
不会被设置。但这只是我的猜测。
编辑 看看你的程序的这个修改:
int main (int argc,char* argv[])
{
using namespace std;
tm t {};
istringstream is ("2016");
// is >> get_time (&t,"%Y %d");
std::string s;
is >> s;
cout << "eof: " << is.eof () << endl
<< "fail: " << is.fail () << endl;
}
我用 get_time
替换了 std::string
,但行为没有改变!字符串已经读入到末尾,所以流状态不能设置为fail
;但是,它到达了文件末尾,因此 eofbit
已设置!
eof: 1
失败:0
我要说的是,类似的现象可以发生在 get_time
内部,然后流的状态会传播到 get_time
的结果。
好的,似乎所有提到的实现都按照 C++11 标准。
这是我对上述程序中发生的事情的理解。
std::get_time() 完成所有准备工作并调用 std::time_get<CharT,InputIt>::get()。
由于第一个格式字符串字符是'%',get()函数调用 do_get() 在解析循环的第一次迭代。
do_get() 在处理 %Y 说明符时读取“2016”并填充 时间对象中的相应字段。除此之外,它根据 标准,因为“在读取一个 字符”。这使得 get() 函数在 do_get() 由于 1b 条件调用(有关详细信息,请参阅 get()),仅为流设置了 eofbit。笔记 %Y 后面的格式部分被完全忽略。
但是,例如,如果我们将输入流从“2016”更改为“2016”(附加空格字符),那么 do_get() 不会设置 eofbit,get() 会读取/匹配流中的空格并在 do_get() 调用后格式化,然后由于 1c 条件同时设置了 eofbit 和 failbit。
通常使用 std::get_time() 读取似乎成功(未设置失败位) 当任一格式字符串与流完全匹配时(可能仍然 里面有一些数据)或者如果在一个 转换说明符已成功应用(与格式的其余部分 字符串被忽略)。