10

我一直在试图弄清楚如何使用scanf(). 它似乎适用于整数,相当简单scanf("%d", &i)

我遇到问题的地方是scanf()在尝试读取输入的循环中使用。例如:

do {
  printf("counter: %d: ", counter);
  scanf("%c %c%d", &command, &prefix, &input);
} while (command != 'q');
  1. 当我输入一个有效结构的输入c P101时,它似乎在提示我之前再次循环。即使只有一个,这似乎也会发生:

    scanf("%c", &c) 
    

    在一个while循环中。在再次提示我之前,它会循环两次。是什么让它循环两次,我该如何阻止它?

  2. 当我输入的输入量较少而以编程方式没有其他字符或数字时q,按 Enter 似乎提示我输入更多。如何scanf()处理单字符和双字符条目?

4

5 回答 5

19

当您输入“ c P101”时,程序实际上会收到“ c P101\n”。大多数转换说明符会跳过包括换行符在内的前导空格,但%c不会。第一次围绕所有内容直到 " \n" 被读取,第二次围绕 "\n" 被读入command, " c" 被读入prefix,并且 " P" 剩下的不是数字,因此转换失败并且 " P101\n" 是留在溪流上。下一次将“ P”存储到命令中,“ 1”存储到前缀中,并且1(从剩余的“ 01”)存储到输入中,“ \n”仍然在流中以供下次使用。

第二种情况发生了类似的事情,当您输入“ q”时,“ q\n”被输入到流中,第一次读取“ q”周围,第二次\n读取“”,仅在第三次调用时第二个“ q”读取,您可以通过在格式字符串的开头添加一个空格字符来再次避免该问题。

更好的方法是使用 fgets() 之类的东西一次处理一行,然后使用 sscanf() 进行解析。

于 2008-10-19T22:59:58.210 回答
1

对于问题 1,我怀疑您的 有问题printf(),因为没有终止“\n”。

的默认行为printf是缓冲输出,直到它有一个完整的行。除非您明确更改stdout.

对于问题 2,您刚刚遇到了scanf(). 除非您的输入与您指定的扫描字符串完全匹配,否则您的结果将与您期望的完全不同。

如果您有一个选项,您将通过忽略scanf()并进行自己的解析获得更好的结果(以及更少的安全问题)。例如,用于fgets()将整行读入字符串,然后处理字符串的各个字段——甚至可能使用sscanf().

于 2008-10-19T22:40:38.763 回答
1

真的坏了!我不知道

#include <stdio.h>

int main(void)
{
    int counter = 1;
    char command, prefix;
    int input;

    do 
    {
        printf("counter: %d: ", counter);
        scanf("%c %c%d", &command, &prefix, &input);
        printf("---%c %c%d---\n", command, prefix, input);
        counter++;
    } while (command != 'q');
}
counter: 1: a b1
---a b1---
counter: 2: c d2
---
 c1---
counter: 3: e f3
---d 21---
counter: 4: ---e f3---
counter: 5: g h4
---
 g3---

输出似乎符合罗伯特的回答。

于 2008-10-19T23:01:08.487 回答
1

一旦你有了包含该行的字符串。即“C P101”,可以使用sscanf的解析能力。

见: http ://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html

于 2008-10-19T23:14:28.087 回答
0

也许使用 while 循环而不是 do...while 循环会有所帮助。这样,在执行代码之前会测试条件。试试下面的代码片段:

 while(command != 'q')
    {
        //statements
    }

此外,如果您提前知道字符串长度,则“for”循环比“while”循环更容易使用。还有一些更棘手的方法来动态确定长度。

作为最后的咆哮:scanf()不“烂”。它做它所做的,仅此而已。

gets()函数非常危险(尽管对无风险应用程序很方便),因为它本身并不对输入进行任何检查。它通常被称为漏洞利用点,特别是缓冲区溢出攻击,覆盖未分配给该变量的寄存器中的空间。因此,如果您选择使用它,请花一些时间进行一些可靠的错误检查/纠正。

但是,几乎总是应该使用fgets()POSIX 或 POSIX来读取该行——注意这两个函数都在输入字符串中包含换行符,这与. 您可以从read by或using中删除尾随换行符——这很可靠。getline()gets()stringfgets()getline()string[strcspn(string, "\n")] = '\0';

于 2012-11-20T11:00:39.890 回答