使用C中的scanf解析输入

时间:2008-10-19 22:35:21

标签: c scanf

我在尝试弄清楚如何使用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()处理单字符和双字符条目?

5 个答案:

答案 0 :(得分:20)

当您输入“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()进行解析。

答案 1 :(得分:1)

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

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

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

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

答案 2 :(得分: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---

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

答案 3 :(得分:1)

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

请参阅: http://www.cplusplus.com/reference/clibrary/cstdio/sscanf.html

答案 4 :(得分:0)

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

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

另外,如果您提前知道字符串长度,'for'循环可以比'while'循环更容易使用。还有一些更复杂的方法来动态确定长度。

作为最后的咆哮:scanf()不会“吮吸”。它做它做的事,就是这样。 fgets()非常危险(虽然对于无风险的应用程序很方便),因为它本身不会对输入进行任何检查。它通常被称为利用点,特别是缓冲区溢出攻击,覆盖未分配给该变量的寄存器中的空间。因此,如果您选择使用它,请花一些时间进行一些可靠的错误检查/纠正。

希望这可以帮助将来的人,因为我认为你现在已经处理好了! ;)