【问题标题】:Missing value with scanf in C89C89 中 scanf 的缺失值
【发布时间】:2017-10-29 12:16:11
【问题描述】:

希望你们能帮助我解决我遇到的问题。

所以我有这个 C 代码 sn-p:

int i = 0;
int q = 0;

scanf("%d %d", &i, &q);

当用户输入4 5时,值4和5分别存储在i和q中。

但是当用户输入99.99时,99存储在i中,但是该点之后的其他99却丢失了。我确实知道 scanf 是如何工作的,并且我知道 scanf 在. 之后会停止扫描,但是第二个值去哪里了?

即使我这样做:

int i = 0;
int q = 0;
int k = 0;


scanf("%d %d", &i, &q);
scanf("%d", &k);

我仍然无法获得第二个值。第二个值去哪里了,我怎样才能将它存储在我的变量中?

【问题讨论】:

  • 正确,但我想将 99.99 存储为 2 个 int 值而不是单个浮点值 99 和 99,这就是我想要第二个值的原因
  • @xing 但是为什么当我在第二次 scanf 调用中执行“.%d”时我的值没有被存储?它应该匹配待处理的输入流,对吧?
  • 你需要检查scanf()的返回值。调试时,将其捕获到变量中并打印并进行测试。它会告诉你发生了什么。当你得到 1 而不是 2 时,你就知道你遇到了问题。

标签: c scanf


【解决方案1】:

它正在尝试查找整数,但它不是留在stdin 中的整数,因此它失败了。

如果这样做,您可以获得所需的行为

int a,b;
scanf("%d.%d",&a,&b);
printf("%d,%d",a,b);

这会按预期打印99,99

供您参考,

  • scanf 在第一个不匹配处停止,其余目标对象保持不变。

  • 同样,不匹配的数据留在输入缓冲区中,可用于后续使用 scanf 等读取。

  • 您还可以检查返回值以确定有多少“项目”scanf 匹配。

【讨论】:

    【解决方案2】:

    scanf() 不会忽略.,而希望读取int。我建议您使用fgets(),然后尝试使用sscanf() 进行解析,这是一种更好的方法。比如:

    #include <stdio.h>
    
    int main(void)
    {
       int i = 0;
       int q = 0;
       char line[256];
    
       if (fgets(line, sizeof line, stdin)) {
          if (sscanf(line, "%d.%d", &i, &q) == 2) {
             printf("i=%d, j=%d\n", i, q);
          } else {
             printf("Coudn't scan 2 ints\n");
          }
       } else {
          printf("Couldn't read a line\n");
       }   
    }
    

    请注意,如果缓冲区中有空间,fgets() 也会读入换行符。在这种情况下没关系。 但您需要注意这一点,并且在某些情况下可能是不可取的(例如,读取 name)。

    另见:Why does everyone say not to use scanf? What should I use instead?

    【讨论】:

    • 更好?那是你的意见;您链接到的文章没有这样说,Torek 的解释和峰会的解释也没有。那些著名的专家意识到,就像 C 语言中的许多工具一样,scanf 很容易被滥用并带来不良后果。 你不能提出不应该使用scanf 的论点,然后提出C 中的其他一切都非常好和花花公子的论点,对吗?按照你的逻辑,也许我们应该停止使用 C
    • 当输入超过 256 个字符时,您的 fgets-abusing 代码无法像 scanf 代码一样更正流...不用担心,因为最简单的方法是更正该问题正在使用scanf... 你需要类似:if (!line[strcspn(line, "\n")]) { scanf("%*[^\n]"); scanf("%*c"); }。该行代码的唯一替代方案引入了不必要的变量,就像您的中间变量 line
    • 没有人声称 C 是完美的;有人对scanf 的评价是很难正确地使用它。而fgets() 提供了对错误检查(例如无效输入)的更好 控制。建议避免 scanf 并不意味着“然后证明C 中的所有其他内容都很好”或“停止使用C”。
    • 正确使用它们并不...至少,不比正确使用数组或避免比赛条件正确。一旦你意识到这种模式,它就会成为习惯。这对我们程序员来说是一种优势;我们非常擅长建立对初学者来说可能违反直觉的习语,但可以提供完全合理的解释。
    • 正如我所展示的,fgets 同样容易被滥用。另一个普遍建立的易于滥用功能是goto,尽管对它的所有批评都存在完全有效的用例,它是最合适的工具... 您应该始终使用最合适的工具,基于这个答案,我不认为这是你的心态。
    【解决方案3】:

    令 X 为以下语句...

    我确实知道 scanf 的工作原理

    令 Y 为以下语句...

    ...但是第二个值去哪里了?

    X 和 Y 不对应。要么你知道scanf 的返回值,要么你不知道scanf 是如何工作的。这是底线。


    ...但是第二个值去哪里了?

    根据the fscanf manual

    fscanf() 函数应依次执行格式的每个指令。如果指令失败,如下所述,函数将返回。失败被描述为输入失败(由于输入字节不可用)或匹配失败(由于输入不当)。

    空格指令与. 输入不匹配,这将导致匹配失败,scanf 将返回,这意味着. 字节之外的任何内容都将消耗来自@ 987654328@。事实上,scanf 甚至会在 . 返回之前将. 放回 stdin... 这不是很好吗? p>

    我想你明白这一点(因为你说“我确实知道 scanf 是如何工作的”),但你似乎在询问第二个 value,由于先前的匹配失败...因此,您的第一个问题的答案是,第二个值不存在。它还不存在,因为在它之前存在输入格式错误。

    虽然我们在讨论 scanf、返回值和匹配失败的主题,但对于剩下的问题,我将假设您的代码实际上看起来更像这样:

    int i = 0;
    int q = 0;
    if (scanf("%d %d", &i, &q) != 2) {
        puts("Discarding invalid input");
        scanf("%*[^\n]");      // discard up to the next newline and...
        getchar();             // discard that newline, too.
                               // XXX: You might want to `return` or `continue` or something
    }
    

    ...我怎样才能将它存储在我的变量中?

    如前所述,空格指令与句点不匹配。您需要将其替换为按您的意图操纵输入流的东西......这对我来说很难回答,因为我没有这些信息,但我建议:拆分两个 conversions 对scanf 进行两次单独的调用。这应该在两次调用scanf 之间为您提供一个不错的位置,以插入您的周期处理代码。

    以下是我为这种输入采用的习语的前后对比:

    // BEFORE                              // AFTER
    if (scanf("%d %d", &i, &q) != 2) {     if (scanf("%d", &i) != 1) {
    // ...                                     puts("Invalid first input...");
                                               // XXX: Handle this???
                                               // XXX: `return` or `continue` or something
                                           }
                                           // XXX: Read a character, check if it's a space (or a dot, or something else which you have in your mind and I don't have in mine)
                                           if (scanf("%d", &q) != 1) {
                                               // ...
    

    处理错误期间可以很简单,例如退出进程或调用getchar() 丢弃单个字符,然后调用continueing 以重试。这些解决方案无法解决所有用户输入错误,甚至可能让一些用户感到困惑或沮丧。我建议坚持使用 丢弃其余行 方法,至少在您了解所有可供用户输入的替代方法 以及它们各自擅长什么之前。毕竟,人们倾向于避免使用让他们感到困惑的软件......

    ...因此人们倾向于不鼓励 scanf... 因为我猜 没有文献可以设定标准,因此 没有办法纠正我与scanf相关的误解

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-22
      • 2017-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-10
      • 2020-10-19
      相关资源
      最近更新 更多