【问题标题】:How can I catch a format error from strftime as an exception?如何从 strftime 捕获格式错误作为异常?
【发布时间】:2020-10-29 08:01:49
【问题描述】:

我在 VS2013 C++ 程序中使用::strftime()。有没有办法正确处理"invalid format" 错误,而不会有停止整个程序的风险?

当我以错误的格式运行如下函数的代码时,比如"%Y- %- m-%d",它是“游戏结束”。我明白了:

“调试断言失败...格式指令无效”

我只能停止应用程序(单元测试套件)。

有没有办法把它变成异常、errno 代码等,以便我可以正确处理这种情况?

或者,我应该使用其他功能吗?

或者,在调用strftime()之前,我必须手动检查格式字符串吗?

using namespace std;
string foo( string fmt ) // fmt: user supplied format - may be invalid
{
  if (fmt.empty()) return "";
  struct tm  myTm = ...; // get it some how
  size_t n = 0;
  size_t buflen = 2 * fmt.size();
  while (buflen < 100) {
    vector<char> buf( buflen, '\0' );
    try {
      n = ::strftime( buf.data(), buf.size(), fmt.c_str(), &myTm );
      if (n>0)
        return string( buf.data(), buf.data() + n );
      if ((n == 0) && (34 == errno)) {
        // 34 == "Result too large" (at least in VS2013)
        buflen *= 2; // try with larger buffer
        continue;
      }
      //   I NEVER REACH THIS POINT!  :-(
      throw runtime_error( "please check your format string!" );
    }
    catch (...) {
      //   I NEVER REACH THIS POINT!  :-(
      throw runtime_error( "please check your format string" );
    }
  }
  throw runtime_error( "max buffer size reached" );
}

【问题讨论】:

  • strftime 是 C 库中的一个函数,作为一个 C 函数,它对异常一无所知,也不能抛出异常。
  • 顺便说一句,用户提供的格式通常是一个非常糟糕的主意。它会打开您的代码以进行可能的攻击。
  • 我知道,这是一个 C 函数。所以我希望得到一个 errno 左右。但我没有。 :-(
  • 不一定是例外。我想要的只是处理错误格式错误 - 但 VS2013 迫使我停止程序(也在发布模式下)。
  • 您为什么要将用户提供的格式字符串传递给strftime?您是否希望您的用户逐字输入?

标签: c++ error-handling visual-studio-2013 strftime


【解决方案1】:

strftime() 文档说:

此函数验证其参数。如果 strDest、format 或 timeptr 是空指针,或者如果 timeptr 寻址的 tm 数据结构无效(例如,如果它包含超出范围的时间或日期值),或者如果格式字符串包含无效格式代码中,将调用无效参数处理程序,如Parameter Validation 中所述。如果允许继续执行,则函数返回 0 并将 errno 设置为 EINVAL。

Parameter Validation 文档说:

参数处理程序无效

当 C 运行时库函数检测到无效参数时,它会捕获有关错误的一些信息,然后调用包装无效参数处理程序调度函数的宏,该函数是 _invalid_parameter、_invalid_parameter_noinfo 或 _invalid_parameter_noinfo_noreturn 之一。调用的调度函数取决于您的代码是分别是调试版本、零售版本还是错误不可恢复。

在调试版本中,无效参数宏通常会在调用调度函数之前引发失败的断言和调试器断点。当代码执行时,根据操作系统和运行时库版本,可以在具有“Abort”、“Retry”和“Continue”或类似选项的对话框中向用户报告断言。这些选项允许用户立即终止程序、附加调试器或让现有代码继续运行,这会调用调度函数。

无效参数处理程序调度函数依次调用当前分配的无效参数处理程序。默认情况下,无效参数调用 _invoke_watson 导致应用程序“崩溃”,即终止并生成一个小型转储。如果操作系统启用,则会出现一个对话框,询问用户是否要将故障转储加载到 Microsoft 进行分析。

可以通过使用函数_set_invalid_parameter_handler_set_thread_local_invalid_parameter_handler 将无效参数处理程序设置为您自己的函数来更改此行为。如果您指定的函数没有终止应用程序,则控制权返回给接收到无效参数的函数。在 CRT 中,这些函数通常会停止函数执行,将 errno 设置为错误代码,并返回错误代码。很多情况下errno值和返回值都是EINVAL,表示参数无效。在某些情况下,会返回更具体的错误代码,例如作为参数传入的错误文件指针的 EBADF。有关 errno 的详细信息,请参阅 errno、_doserrno、_sys_errlist 和 _sys_nerr。

【讨论】:

  • 这个答案不过是复制的文档,也可以在评论中链接。您能否稍微消化一下,并可能通过示例解释 OP 如何从格式错误中恢复?
  • @alterigel 阅读和理解文档的能力是程序员需要的一项非常重要的能力。
  • 感谢您将我指向“参数验证”部分。这是 MS 特有的,对吧?
  • 我尝试了自己的处理程序 - 工作正常。但我可能会在调用 :strftime 之前使用手写格式检查
  • @ToniSchilling C11 标准对strftime 这么说:“如果转换说明符不是上述之一,则行为未定义。”所以每个库实现者都会有自己的特定功能。
猜你喜欢
  • 2013-01-28
  • 2019-09-08
  • 2015-06-11
  • 2014-03-21
  • 2010-12-14
  • 2011-04-18
  • 2012-02-01
  • 2017-07-05
  • 1970-01-01
相关资源
最近更新 更多