6

Mark Lakata指出我的问题中没有正确定义垃圾之后,我想出了这个。我会保持更新以避免混淆。


我正在尝试获取一个函数,我可以在提示用户输入之前调用该函数,例如printf("Enter your choice:);follow a scanf,并确保只有在提示之后输入的内容才会被扫描scanf为有效输入。

据我所知,所需的功能是完全刷新标准输入的东西。这就是我想要的。因此,出于此功能的目的,"garbage"用户输入中的所有内容,即在该用户提示之前的整个用户输入。


在 C 中使用scanf()时,总是存在输入缓冲区中存在额外输入的问题。所以我一直在寻找一个在每次 scanf 调用后调用的函数来解决这个问题。我用这个这个这个这个来得到这些答案

//First approach
scanf("%*[^\n]\n");

//2ndapproach
scanf("%*[^\n]%*c");

//3rd approach
int c;
while((c = getchar()) != EOF) 
    if (c == '\n') 
        break;

所有这三个都在尽我所能找到,并通过参考文献进行。但是在我的所有代码中使用其中任何一个之前,我想知道其中是否有任何错误?

编辑:

感谢Mark Lakata在 3rd 中的一个错误。我在问题中纠正了它。

编辑2:

Jerry Coffin回答后,我在代码中使用该程序测试了第 2 种方法:使用 GNU GCC 编译器阻止 IDE 12.11(编译器设置中未说明版本)。

#include<stdio.h>

int main()
{
    int x = 3; //Some arbitrary value
    //1st one
    scanf("%*[^\n]\n");
    scanf("%d", &x);
    printf("%d\n", x);

    x = 3;
    //2nd one
    scanf("%*[^\n]%*c");
    scanf("%d", &x);
    printf("%d", x);
}

我使用了以下 2 个输入

第一个测试输入(2 个换行符,但垃圾输入中间没有空格)

abhabdjasxd


23
bbhvdahdbkajdnalkalkd



46

printf首先,我通过语句得到以下输出

23
46

即两个代码都工作正常。

第二个测试输入:(垃圾输入中间有空格的2个换行符)

hahasjbas asasadlk


23
manbdjas sadjadja a


46

printf第二次我通过语句得到以下输出

23
3

因此我发现第二个不会处理额外的垃圾输入空白。因此,它对垃圾输入并不是万无一失的。

我决定尝试第三个测试用例(垃圾在非空白字符之前和之后包括换行符)

``
hahasjbas asasadlk


23

manbdjas sadjadja a


46

答案是

3
3

即在这个测试用例中都失败了。

4

5 回答 5

9

前两个略有不同:它们都读取并忽略所有字符,直到换行。然后第一个跳过所有连续的空格,因此在它执行后,您读取的下一个字符将是非空格。

第二个读取并忽略字符,直到遇到换行符,然后再读取(并丢弃)一个字符。

如果您有(例如)双倍行距文本,则差异将显示出来,例如:

line 1

line 2

假设你读到了第 1 行中间的某个地方。如果你然后执行第一个,你读入的下一个字符将是第 2 行的 'l'。如果你执行第二个,你读入的下一个字符将成为第 1 行和第 2 行之间的换行符。

至于第三个,如果我要这样做,我会做类似的事情:

int ch;
while ((ch=getchar()) != EOF && ch != '\n')
    ;

...是的,这确实可以正常工作-&&强制一个序列点,因此首先评估其左操作数。然后是一个序列点。然后,当且仅当左操作数计算为 时true,它才会计算其右操作数。

至于性能差异:由于您首先要处理 I/O,所以几乎没有合理的问题是所有这些都将始终受 I/O 限制。尽管其明显的复杂性,scanf(和公司)通常是经过多年使用并经过仔细优化的代码。在这种情况下,手动循环可能会慢很多(例如,如果 for 的代码getchar没有内联扩展),或者它的速度可能大致相同。如果编写标准库的人不称职,那么它有可能显着更快的唯一方法是。

至于可维护性:IMO,任何声称知道 C的人都应该知道scanf. 这既不是新的科学,也不是火箭科学。任何不知道它的人都不是一个称职的 C 程序员。

于 2013-07-03T15:10:13.020 回答
3

前两个示例使用了我什至不知道存在的 scanf 功能,而且我敢肯定很多其他人都不知道。能够支持未来的功能很重要。即使它是一个众所周知的功能,与您的第三个示例相比,读取格式字符串的效率和难度也会降低。

第三个例子看起来不错。

(编辑历史:我犯了一个错误,说 ANSI-C 不保证 && 的从左到右评估并提出更改。但是,ANSI-C 确实保证 && 的从左到右评估。我不确定关于 K&R C,但我找不到任何参考,也没有人使用它......)

于 2013-07-03T05:52:50.783 回答
1

许多其他解决方案的问题是,当没有任何东西可以刷新时,它们会导致程序挂起并等待输入。等待EOF是错误的,因为在用户完全关闭输入之前您不会得到它!

在 Linux 上,以下将执行非阻塞刷新:

// flush any data from the internal buffers
fflush (stdin);

// read any data from the kernel buffers
char buffer[100];
while (-1 != recv (0, buffer, 100, MSG_DONTWAIT))
  {
  }

Linux 手册页说fflushonstdin是非标准的,但“大多数其他实现的行为与 Linux 相同”。

MSG_DONTWAIT标志也是非标准的(recv如果没有要传递的数据,它会导致立即返回)。

于 2013-07-05T16:45:14.930 回答
-1

你应该使用getline/ getchar

#include <stdio.h>

int main()
{
  int bytes_read;
  int nbytes = 100;
  char *my_string;

  puts ("Please enter a line of text.");

  /* These 2 lines are the heart of the program. */
  my_string = (char *) malloc (nbytes + 1);
  bytes_read = getline (&my_string, &nbytes, stdin);

  if (bytes_read == -1)
    {
      puts ("ERROR!");
    }
  else
    {
      puts ("You typed:");
      puts (my_string);
    }

  return 0;
于 2013-06-22T07:37:34.187 回答
-1

我认为,如果您仔细查看此页面的右侧,您会看到许多与您的问题相似的问题。您可以在 Windows 上使用 fflush()。

于 2013-07-03T06:23:54.973 回答