std::get_time() 对于“短”输入的正确行为是什么

问题描述

我试图了解 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 eofbithttps://en.cppreference.com/w/cpp/io/ios_base/iostate 部分,eof 位被设置的情况之一是当

std::get_time I/O 操作符和任何 std::time_get 解析函数:time_get::gettime_get::get_timetime_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() 读取似乎成功(未设置失败位) 当任一格式字符串与流完全匹配时(可能仍然 里面有一些数据)或者如果在一个 转换说明符已成功应用(与格式的其余部分 字符串被忽略)。