【问题标题】:What is the correct behavior of std::get_time() for "short" inputstd::get_time() 对于“短”输入的正确行为是什么
【发布时间】:2023-03-14 21:57:01
【问题描述】:

我试图了解 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" 表明这是故意修复的。

有人可以澄清上述标准库是否(以及为什么)行为正确,如果是这样,它应该如何检测格式字符串未完全使用。

【问题讨论】:

    标签: c++11 std date-parsing


    【解决方案1】:

    我无法以 100% 的准确度解释,但我可以尝试解释为什么函数的行为与观察到的一样。

    我怀疑您的情况下没有设置eofbit,即1c,因为情况1b优先:

    b) 出现解析错误 (err != std::ios_base::goodbit)

    根据https://en.cppreference.com/w/cpp/io/ios_base/iostateThe eofbit部分,设置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&gt;&gt;(std::string&amp;) 读取它,达到 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的结果。

    【讨论】:

    • 如果我错了,请纠正我,但根据你的逻辑,流应该以设置失败位和未设置 eofbit 结束,因此程序应该打印eof: 0fail: 1。但是,正如我所说,它会打印出eof: 1fail: 0
    • 我在答案中添加了一个部分,在 EDIT 下方。
    【解决方案2】:

    好的,似乎所有提到的实现都按照 C++11 标准。

    这是我对上述程序中发生的事情的理解。

    std::get_time() 做所有准备工作并致电std::time_get<CharT,InputIt>::get()

    由于第一个格式字符串字符是'%',get()函数调用 do_get() 在解析循环的第一次迭代中。

    do_get() 在处理 %Y 说明符时读取“2016”并填充 时间对象中的相应字段。除此之外,它设置 eofbit 根据 标准,因为“在读取一个 字符”。这使得 get() 函数在 由于 1b 条件而调用 do_get() (有关详细信息,请参阅get()),仅为流设置了 eofbit。笔记 %Y 后面的格式部分被完全忽略。

    但是,例如,如果我们将输入流从“2016”更改为“2016”(附加空格字符),那么 do_get() 不会设置 eofbit,get() 会读取/匹配流中的空格并在 do_get() 调用之后进行格式化,然后由于 1c 条件而退出,同时设置了 eofbit 和 failbit。

    通常使用 std::get_time() 读取似乎成功(未设置故障位) 当任一格式字符串与流完全匹配时(可能仍然 有一些数据)或者如果在一个之后到达流的末尾 转换说明符已成功应用(与格式的其余部分 字符串被忽略)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-19
      • 1970-01-01
      • 1970-01-01
      • 2015-11-08
      • 2013-01-07
      • 1970-01-01
      • 1970-01-01
      • 2014-10-01
      相关资源
      最近更新 更多