【问题标题】:How to handle less input than expected with scanf - C如何使用 scanf - C 处理比预期更少的输入
【发布时间】:2018-03-05 11:13:08
【问题描述】:

我需要接收来自用户输入的两种形式,charintfloatcharint,但我不知道会给出哪一种。我试过这个:

int main(){
    char letter;
    int num;
    float value;
    while(getchar()!=EOF){
        scanf(" %c %d %f", &letter, &num, &value);
        printf("%c\n", letter);
        printf("%d\n", num);
        printf("%f\n", value);
    }
    return 0;
}

问题是当我给出这样的输入时:

? 12345
g 12345 45.6
? 12345

Output given:                      Expected Output:
1                                  ?
2345                               12345
0.000000                           0.000000
1                                  g
2345                               12345
45.599998                          45.599998
?                                  ?
12345                              12345
45.599998                          45.599998

为什么部分数字会代替charchar 会被忽略?有没有办法解决这个问题?

【问题讨论】:

  • 我认为发生的事情是您不小心使用getchar()!=EOF 读取了第一个字符。
  • 有没有办法避免这种情况?我需要使用 ctrl+d 逗号停止程序,这就是它存在的原因。
  • 你的主循环应该沿着int numvals; while ((numvals = scanf("…", …)) >= 2) { … }的行。只有当您设法转换 2 或 3 个值时,循环才会继续。您应该始终检查 scanf() 等函数的返回值;忽略它是一个错误。
  • 嗯,是的 - 你从其他人那里得到了很好的答案。
  • 这个错误实际上是 getchar()!=EOF,使用 Jonathan Leffer 的建议,它得到了修复,如果你们中的任何人想给出它作为答案,我会将其标记为正确,感谢两者。

标签: c input scanf


【解决方案1】:

这里有几个问题:

  1. 缺少的字符。当您第一次执行while(getchar()!=EOF) 时,您正在使用流中的第一个字符,恰好是'?'。随后的scanf() 无法再检索到它,因此它会抓取下一个非空白字符,恰好是以下数字的'1'

    读取'1'后,解析数字的其余部分,提供结果2345

    下面的转换尝试解析一个浮点数,但是下一个非空白字符是g,因此无法读取浮点数,转换失败。

  2. 你没有检查,有多少转化是成功的。 scanf() 尽职尽责地返回成功转换的次数,但你忽略了它。

    如果您检查了返回值,您会发现第一个scanf() 调用返回2,因为字符和整数转换成功完成,但浮点转换失败。

    同样,第二个scanf() 调用应该返回3,表明所有三个转换都成功了。最后一次调用scanf() 应该再次返回2

  3. while(getchar()!=EOF) 中的字符在第二行再次咬住你,这次是 'g'scanf() 有机会阅读之前从流中删除。

    scanf() 的第二次调用成功终止后,'\n' 字符留在流中。因此,下面的while(getchar()!=EOF) 将其吞噬,第三个scanf() 调用最终可以正确获取行中的第一个字符('?')。

  4. float转换在第三行再次失败,对应的变量保持不变,忽略scanf()的返回值无法检测到这种情况。

长话短说:

  • 每个getchar() 调用都会消耗一个scanf() 无法再读取的字符。

  • 任何忽略返回值的scanf() 调用都是一个错误。如果不检查,您将无法知道您实际上阅读了任何内容。


顺便说一句:man scanfman getchar 本来可以告诉你我刚才所说的一切。知道如何阅读手册页是值得的。

【讨论】:

  • 感谢您的回答,但正如我之前所说,问题已经在 cmets 中解决了。无论如何,我会将您的答案标记为正确的答案,因为首先解决问题的人似乎不想声称它。
【解决方案2】:

在这种情况下,最好先将用户输入检索为 char*(类似 fgets),然后使用 sscanf(不是 scanf,sscanf)对其进行解析。 您必须检查 sscanf 的返回值才能知道输入用户的格式是否正确。

如果需要检查解析后是否有垃圾值(如“g 12345 45.6garbagevalue”),可以在后面加上一个 %c 来检查值是否发生了变化。

char   letter;
int    integerNumber;
double floatingNumber;
char   end;
char *userInput = ...;

end = '\0';
if (sscanf(userInput, "%c %d %f%c", &letter, &integerNumber, &floatingNumber, &end) == 3 && end == '\0') {
    // First form
} else {
    end = '\0';
    if (sscanf(userInput, "%c %d%c", &letter, &integerNumber, &end) == 2 && end == '\0') {
        // Second form
    } else {
        // Error : Input not strictly "%c %d %f" or "%c %d"
    }
}

你觉得不错?

【讨论】:

  • 以防万一您不知道:scanf 返回成功读取的项目数,因此在这种情况下,它必须返回 3 或 2 才能获得有效值。
  • 我只是不明白一件事,什么是“char *userInput = ...;”应该相等?
  • @MiguelID :我不会为你编写所有代码。我只是给你一些方法来做你想做的 scanf 系列。您可以根据自己的情况将“...”部分替换为相关代码。
  • @Yonlif 是的,我知道,但是如果您希望在 %f 或 %d 之后的用户输入字符串中严格使用 "%c %d %f" 而没有垃圾值,那么代码就是一个示例。此外,如果用户输入的字符串是 "integer float" 会发生什么?返回仍然是 2,按您的方式是“正确的”,但应该有 3 个结果,最终结果是“不正确的”。根据您的情况,您最终可能会得到一个非常严格的 scanf 格式,例如 "%c[ ]%d[ ]%f%c" ,以确保您解析的内容正是您所期望的。
  • 哦,很抱歉,这条评论不是针对您,而是针对问题的作者。只是为了他的信息......
【解决方案3】:

我想知道从键盘读入一个数据块到键盘上的一个字符串,然后用sscanf分析是否会更简单,如下面的大纲所示

#define MAX_INPUT 100

char data_input[MAX_INPUT];
int finished=0, test;

char letter;
int num;
float value;

while (finished==0) {
  // scanf("%s",data_input); //taken out after useful comments below
  fgets(data_input,100,stdin);  //replacement from comments...

  // insert code using sscanf to read in data from user
  test=sscanf(data_input, "%c %d %f", &letter, &num, &value);

  if (test==3) 
  {
    //success - sscanf successfully assigned 3 values... 

    // (ony if you have everything you need then....)
      finished =1; 
  } else if (test==2){
     // insert something to try to test if data from two data entries is ok....

    // (ony if you have everything you need then....)
      finished =1; 
  } else {
    printf("problem with data entry - please type again\n");
  }
}

向@Tom 道歉,因为sscanf 的使用是他首先建议的...

【讨论】:

  • 感谢您的回答,但正如上面的 cmets 所说,Jonathan Leffler 和 Yonlif 的解决方案足以解决我遇到的问题。
  • 而且更关键的是,会导致缓冲区溢出。至少应该是“%99s”(或“%.99s”?我忘了)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-21
  • 2021-09-16
  • 1970-01-01
  • 2018-06-13
  • 2021-01-23
  • 2017-06-14
  • 1970-01-01
相关资源
最近更新 更多