一个典型的习语是:
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(参见"返回值"部分)讨论了四种不同的条件:
档案结尾
阅读错误
匹配失败
解释宽字符的编码错误
前两个问题由feof(..)
和ferror(..)
解决(我假设feof(..)
隐含ferror(..)
),最后一个问题由{{1}设置到errno
。但是我对一个全能的感兴趣(即包括确定是否发生了匹配的故障)。
P.S。看一下上面这个简洁的上下文例子,这可能看起来有点太多了,但是考虑一下真正的实践,你可以快速做出很多改变,并且每次都要记住数以百计这样的小事。你改变一件事,你必须在别的地方改变另一件事。然后很明显,养成消除各种依赖关系的习惯会得到回报。
答案 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
的返回值。是的,不要那样做。它看起来似乎更好,但如果其他人要查看你的代码,他们就不会知道你改变了它,而且看起来似乎是不正确的使用。