其他答案已经正确诊断出scanf()不输出任何数据(特别是不生成任何提示),而问题似乎预计scanf("Letter: %s", y)会输出提示Letter:然后阅读一个答复。还有一些问题,比如不检查来自scanf()的返回值,如果输入多个字符会导致缓冲区溢出。
其中一个答案建议格式字符串中不应出现转换规范以外的字符。这个答案主要是一个反例,展示了其他角色如何至关重要。您可以获取此示例代码并对其进行修改,以提高您对scanf() 系列函数工作原理的理解。请注意,它不是交互式程序;它使用sscanf() 转换数据,就好像数据已被fgets() 读取一样。 练习:为什么只是“几乎好像”而不是简单的“好像”?
上下文
假设您正在处理的文件格式如下:
centre (23, 41) size (19, 42)
假设您要读取这四个数字,您的格式字符串很可能是:
"centre (%d,%d) size (%d,%d)"
这几乎是正确的。但是,该格式无法发现缺少第二个右括号。为了确保最后的括号存在,您需要一个格式字符串,如:
"centre (%d ,%d ) size (%d ,%d %1[)]"
其中的空格允许输入间距的多种变化,而扫描集 (%1[)]) 需要一个右括号。您将测试 scanf() 返回 5 个字段。请注意,如果您使用扫描集 (%*1[)]) 禁止分配,则如果缺少括号,您将不会收到错误指示。这是一个判断要求,您希望在接受有效输入时有多灵活。
代码
#include <stdio.h>
int main(void)
{
int x1, y1, x2, y2;
char str[10];
const char *data[] =
{
"centre ( 19, 43 ) size ( 21, 37 )",
"centre (19, 43) size (21, 37)",
"centre (19, 43) size (21, 37",
"centre(19,43) size(21,37)",
"centre(19,43) size(21,37",
"centre ( 19 , 43 ) size ( 21 , 37 )",
};
enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };
const char *format5[] =
{
"centre (%d ,%d ) size (%d ,%d %[)]",
"centre (%d,%d) size (%d,%d%[)]",
};
enum { NUM_FORMAT5 = sizeof(format5) / sizeof(format5[0]) };
const char *format4[] =
{
"centre (%d ,%d ) size (%d ,%d )",
"centre (%d,%d) size (%d,%d)",
};
enum { NUM_FORMAT4 = sizeof(format4) / sizeof(format4[0]) };
printf("Format 5\n");
for (int i = 0; i < NUM_FORMAT5; i++)
{
printf("Format: <<%s>>\n", format5[i]);
for (int j = 0; j < NUM_DATA; j++)
{
int rc;
printf(" Data: <<%s>>\n", data[j]);
if ((rc = sscanf(data[j], format5[i], &x1, &y1, &x2, &y2, str)) != 5)
printf("!! Failed: scanf() returned %d\n", rc);
else
printf("== Passed: centre(%d,%d) size(%d,%d)\n", x1, y1, x2, y2);
}
}
printf("\nFormat 4\n");
for (int i = 0; i < NUM_FORMAT4; i++)
{
printf("Format: <<%s>>\n", format4[i]);
for (int j = 0; j < NUM_DATA; j++)
{
int rc;
printf(" Data: <<%s>>\n", data[j]);
if ((rc = sscanf(data[j], format4[i], &x1, &y1, &x2, &y2)) != 4)
printf("!! Failed: scanf() returned %d\n", rc);
else
printf("== Passed: centre(%d,%d) size(%d,%d)\n", x1, y1, x2, y2);
}
}
return 0;
}
输出
Format 5
Format: <<centre (%d ,%d ) size (%d ,%d %[)]>>
Data: <<centre ( 19, 43 ) size ( 21, 37 )>>
== Passed: centre(19,43) size(21,37)
Data: <<centre (19, 43) size (21, 37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre (19, 43) size (21, 37>>
!! Failed: scanf() returned 4
Data: <<centre(19,43) size(21,37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre(19,43) size(21,37>>
!! Failed: scanf() returned 4
Data: <<centre ( 19 , 43 ) size ( 21 , 37 )>>
== Passed: centre(19,43) size(21,37)
Format: <<centre (%d,%d) size (%d,%d%[)]>>
Data: <<centre ( 19, 43 ) size ( 21, 37 )>>
!! Failed: scanf() returned 2
Data: <<centre (19, 43) size (21, 37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre (19, 43) size (21, 37>>
!! Failed: scanf() returned 4
Data: <<centre(19,43) size(21,37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre(19,43) size(21,37>>
!! Failed: scanf() returned 4
Data: <<centre ( 19 , 43 ) size ( 21 , 37 )>>
!! Failed: scanf() returned 1
Format 4
Format: <<centre (%d ,%d ) size (%d ,%d )>>
Data: <<centre ( 19, 43 ) size ( 21, 37 )>>
== Passed: centre(19,43) size(21,37)
Data: <<centre (19, 43) size (21, 37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre (19, 43) size (21, 37>>
== Passed: centre(19,43) size(21,37)
Data: <<centre(19,43) size(21,37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre(19,43) size(21,37>>
== Passed: centre(19,43) size(21,37)
Data: <<centre ( 19 , 43 ) size ( 21 , 37 )>>
== Passed: centre(19,43) size(21,37)
Format: <<centre (%d,%d) size (%d,%d)>>
Data: <<centre ( 19, 43 ) size ( 21, 37 )>>
!! Failed: scanf() returned 2
Data: <<centre (19, 43) size (21, 37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre (19, 43) size (21, 37>>
== Passed: centre(19,43) size(21,37)
Data: <<centre(19,43) size(21,37)>>
== Passed: centre(19,43) size(21,37)
Data: <<centre(19,43) size(21,37>>
== Passed: centre(19,43) size(21,37)
Data: <<centre ( 19 , 43 ) size ( 21 , 37 )>>
!! Failed: scanf() returned 1
请注意,当缺少第二个右括号时,“格式 4”下的格式字符串如何接受数据。即使缺少字符,也满足 4 个转换规范。 “格式 5”格式拒绝这些数据行。
示例代码和数据没有演示它,但代码也很乐意读取多个右括号(因为它使用%[)])。这可以通过使用%1[)] 来规定最后只有一个右括号来避免。您还可以使用%n 转换规范和第六个参数(另一个int *)来获取处理的字符数。这将允许您检测扫描停止的位置,从而在所需的输入之后是否有未处理的字符。请注意,%n 转换规范不计入来自scanf() 等的返回值中。这个片段可以粘贴在代码中main()函数的末尾:
printf("\nFormat 6\n");
int len, rc;
const char data6[] = "centre ( 19 , 43 ) size ( 21 , 37 )))";
const char format6[] = "centre (%d ,%d ) size (%d ,%d %1[)]%n";
printf("Format: <<%s>>\n", format6);
printf(" Data: <<%s>>\n", data[5]);
if (sscanf(data6, format6, &x1, &y1, &x2, &y2, str, &len) != 5)
printf("!! Failed: scanf() returned %d\n", rc);
else
printf("== Passed: centre(%d,%d) size(%d,%d) len=%d <<%s>>\n",
x1, y1, x2, y2, len, &data6[len]);
它生成输出:
Format 6
Format: <<centre (%d ,%d ) size (%d ,%d %1[)]%n>>
Data: <<centre ( 19 , 43 ) size ( 21 , 37 )))>>
== Passed: centre(19,43) size(21,37) len=35 <<))>>
总结
如果你理解了为什么会得到每一个结果,那么你对scanf()的理解就不错了。如果您不确定原因,请尝试并阅读规范(例如 POSIX sscanf()),直到您确定自己理解为止。