在不使用字段数的情况下检查scanf(..)成功 - 有办法吗?

时间:2015-08-13 06:50:34

标签: c scanf

一个典型的习语是:

if (scanf("%d%d%d", &a, &b &c) != 3)
    handle_the_failure();

这个字段数3是多余的。如果我将模式和参数更改为scanf(..),但忘记更改3,那就是另一个编译测试调试周期,这是浪费时间。

是否有一个成语允许检查scanf(..)的(绝对)成功而不必编写代码中的字段数?也许是这样的:

scanf("%d%d%d", &a, &b &c);
if (lastScanfFailedInAnyWay())
    handle_the_failure();

documentation(参见"返回值"部分)讨论了四种不同的条件:

  1. 档案结尾

  2. 阅读错误

  3. 匹配失败

  4. 解释宽字符的编码错误

  5. 前两个问题由feof(..)ferror(..)解决(我假设feof(..)隐含ferror(..)),最后一个问题由{{1}设置到errno。但是我对一个全能的感兴趣(即包括确定是否发生了匹配的故障)。

    P.S。看一下上面这个简洁的上下文例子,这可能看起来有点太多了,但是考虑一下真正的实践,你可以快速做出很多改变,并且每次都要记住数以百计这样的小事。你改变一件事,你必须在别的地方改变另一件事。然后很明显,养成消除各种依赖关系的习惯会得到回报。

3 个答案:

答案 0 :(得分:4)

你可以这样做:

bool wrap_the_scanf(char const *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    int result = vscanf(fmt, ap);
    va_end(ap);
    return result == count_specifiers(fmt);
}

其中count_specifiers是一个函数,它读取字符串,查看其中有多少%(将%%计为零)。

答案 1 :(得分:3)

您可以这样做:

int nch = -1;
scanf("%d%d%d%n", &a, &b, &c, &nch);
if (nch == -1)
  handle_the_failure();

答案 2 :(得分:2)

与许多事情一样,这可以通过一些预处理器魔术来完成。首先,我们需要一个宏来计算参数的数量。让@JamesMcNellis

采取this one
// Expand up to 32 arguments if needed
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N 
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1)

如果我们使用所有VA_NARGS参数调用scanf,它将返回字段数,再加上格式说明符的字段数。

现在我们将scanf包装成成功时返回true,失败时返回false:

#define scanf_return_bool(...) (scanf(__VA_ARGS__) == VA_NARGS(__VA_ARGS__)-1)

您可以将其用作:

if (!scanf_return_bool("%d %d", &a, &b))
    handle_error();

一个有趣的说明:您可以将scanf_return_bool重命名为scanf,并有效地覆盖scanf的返回值。是的,不要那样做。它看起来似乎更好,但如果其他人要查看你的代码,他们就不会知道你改变了它,而且看起来似乎是不正确的使用。