使用C中的scanf解析输入

问题描述 投票:10回答:5

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

c scanf
5个回答
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
投票

它真的坏了!我不知道

#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---

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


1
投票

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

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


1
投票

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

printf的默认行为是缓冲输出,直到输出完整的行为止。除非您显式更改stdout上的缓冲。

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

如果您有一个选择,则忽略scanf()并进行自己的分析,将会得到更好的结果(并且安全问题更少)。例如,使用fgets()将整行读入字符串,然后处理字符串的各个字段-甚至可以使用sscanf()


0
投票

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

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

此外,如果您提前知道字符串的长度,那么'for'循环比'while'循环更容易使用。还有一些技巧也可以动态确定长度。

[最后说:scanf()不“吸”。它所做的就是全部。

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

但是,几乎总是使用gets()或POSIX fgets()来读取行-请注意,与fgets()不同,函数都在输入字符串中包括换行符。您可以使用getline()getline()gets()读取的string中删除尾随换行符-这可靠地工作。

© www.soinside.com 2019 - 2024. All rights reserved.