在 while 循环中使用 scanf 函数

7
我正在尝试为编程作业格式化以空格分隔的用户输入。该输入基本上由任意数量的表达式组成,包括“L 整数 整数 整数 整数”和“C 整数 整数 整数”等。例如:“L 1 1 5 7 C 4 5 3”。到目前为止,我已经成功提取了初始字符的整数,并可以使用scanf函数迭代遍历字符串。
char a;
while(scanf("%c", &a) == 1){
    if(a == 'C'){
        int inputX, inputY, inputR;
        scanf("%d %d %d", &inputX, &inputY, &inputR);
        printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(a == 'L'){
        int x1, y1, x2, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
}

不幸的是,尽管这样输出所需的整数,但循环(和用户输入提示)并没有终止。

请问有人能告诉我为什么会出现这种情况吗?


你在输入结束时是否输入 EOF(在Linux中通过Ctrl+D,在Windows中通过Ctrl+Z)? - timrau
@timrau 输入 EOF 会在程序运行时结束输入,但我也希望它能在按下 Enter 键时终止。 - Julian Laval
4个回答

20
这是因为\n总是存在,使得scanf("%c", &a) == 1始终为真。
改变你的

while(scanf("%c", &a) == 1) 

while(scanf(" %c", &a) == 1)  
     //      ^space before format specifier.  

%c之前加一个空格将会吃掉scanf留下的这个\n(在按下Enter键时)。


这似乎不起作用;添加空格并没有改变任何东西。有什么想法吗? - Julian Laval
@haccks 我写了一个程序,它读取一些字符。printf("\n 1. 给出一个字符:\n"); scanf("%c", &ch); while(ch != '\0'){ ... printf("\n 2. 给出一个字符:\n"); scanf(" %c", &ch); }但是运行这个程序时,第二个 printf 会打印两次。在 %c 前添加一个空格后,它才能正常工作!你能解释一下为什么我们必须在 %c 前添加一个空格,以便第二个 printf 只打印一次吗? - Mary Star
4
原因很简单,就在上面的回答中我已经解释过了。当你按下“Enter”键时,一个换行符\n将进入输入缓冲区。如果输入字符是a,则输入缓冲区将包含a\n。第一个scanf将读取a,留下\n在缓冲区中供下一次调用scanf使用。进入循环后,你的第二个scanf将从输入缓冲区读取这个剩余的\n,循环再次运行而不等待你的输入,这就是为什么你会看到两次“Give a character”的提示。 - haccks
1
在%c前加一个空格会将scanf留下的\n(或任意数量的空白符)吞掉。 - haccks
如果我想读取1 2\n,我应该使用scanf("%d %d "),即为每个"%d"附加一个空格,对吗? - VimNing
1
哇!@haccks,你太棒了。我遇到了完全相同的问题,你的帖子帮助我解决了它。 - Stats_Lover

4
原因是 scanf 直接从标准输入读取,处理完该行后会阻塞并等待用户输入。你需要做的是在 while 循环中读取该行并处理该行。我已经修改了你的代码,如下所示。
char a;
char line[1024];

fgets(line, 1023, stdin);   // leave 1 character for null terminator
while(sscanf(line, "%c", &a) == 1){
    if(a == 'C'){
        int inputX, inputY, inputR;
        sscanf(line, "%d %d %d", &inputX, &inputY, &inputR);
        printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(a == 'L'){
        int x1, y1, x2, y2;
        sscanf(line, "%d %d %d %d", &x1, &y1, &x2, &y2);
        printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
}

2

结合其他帖子的一些特点和一些补充。
sscanf()内使用fgets()%n。一定要检查sscanf()的结果。

char line[1024];
while (fgets(line, sizeof line, stdin) != NULL)) {
  char *s = line; 
  char Type;
  int n;
  while(sscanf(s, " %c%n", &Type, &n) == 1) {
    s += n;
    if(Type == 'C') {
      int inputX, inputY, inputR;
      if (3 != sscanf(s, "%d %d %d%n", &inputX, &inputY, &inputR, &n)) {
        Handle_Syntax_Error();
      }
      s += n;
      printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(Type == 'L') {
      int x1, y1, x2, y2;
      if (4 != sscanf(s, "%d %d %d %d%n", &x1, &y1, &x2, &y2, &n)) {
        Handle_Syntax_Error();
      }
      s += n;
      printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
    else {
      Handle_Syntax_Error();
    }
  }
} 

1
char a;
    while(scanf("%[LC]", &a) == 1){
    if(a == 'C'){
        int inputX, inputY, inputR;
        scanf("%d %d %d%*c", &inputX, &inputY, &inputR);
        printf("%d %d %d\n", inputX, inputY, inputR);
    }
    else if(a == 'L'){
        int x1, y1, x2, y2;
        scanf("%d %d %d %d%*c", &x1, &y1, &x2, &y2);
        printf("%d %d %d %d\n", x1, y1, x2, y2);
    }
}

"

"%[LC]" 用于确保只会扫描到 'L' 或 'C'。根据输入整数的位数,将扫描 3 或 4 个整数,之后无论是空格、回车还是任何一个字符都将被丢弃 "%*c"。在此之后,除了 'L' 或 'C' 的任何字符都将打破循环。

"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接