使用fgets从stdin读取,如果输入大于大小则读取bug

时间:2015-06-12 20:03:39

标签: c

我正在使用提示创建命令行应用程序。只要输入适合缓冲区,它就可以正常工作,但是当输入较大时,我会得到一些奇怪的行为。下面是一个简化的最小示例,它具有相同的错误。

int main()
{
    for (;;) {
        const int size = 8;
        char str[size];

        printf("> ");
        fgets(str, size, stdin);

        toupper_str(str);
        printf("Result: %s\n", str);
    }
}

如果输入小于size,则可以正常工作。

> asdf
Result: ASDF
> 

当输入较大时,处理范围内输入的部分,并且在下一循环迭代中,立即从fgets返回给定输入的其余部分。这导致输入的那部分也被处理和一些奇怪的输出。

> asdfghjk
Result: ASDFGHJ
> Result: K

> 

通过将最后一个字符与换行符进行比较,我可以看到输入是否大于或等于大小。 fgets只要合适,就会保留换行符。

fgets(str, size, stdin);
if (str[strlen(str) - 1] != '\n') {
    fprintf(stderr, "Input too long\n");
}

当检测到这个时,如何阻止它在下一次迭代中读取剩余的太长时间的输入?

我在这里看到过类似的问题,但没有人提出同样的问题。

2 个答案:

答案 0 :(得分:4)

  

如何阻止它在下一次迭代中读取剩余的太长时间的输入?

代码需要1)检测输入是否“太长”2)消耗额外的输入。

fgets()不会溢出缓冲区。如果它确实填充了缓冲区,则缓冲区中的最后一个char'\0'。所以在阅读之前将其设置为非'\0'。然后代码知道整个缓冲区是否已填满。然后检查前面的char是否为'\n'。如果它不是行尾,则char

中可能存在其他stdin
char str[100];  // Insure buffer is at least size 2
for (;;) {
  str[sizeof str - 1] = `x`;
  if (fgets(str, size, stdin) == NULL) {
    // No more to read or IO error
    break;
  }
  int extra_data_found = 0;
  if (str[sizeof str - 1] == '\0' && str[sizeof str - 2] != '\n') {
    // Cope with potential extra data in `stdin`: read and toss
    int ch;
    while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
      extra_data_found = 1;
    }
  }
  // Use `str` as needed, noting if additional unsaved data found
  foo(str, extra_data_found);
}

注意:如果文件错误,fgets()返回NULLstr的内容未定义。

注意:代码可以使用str[sizeof str - 1] == '\0'而不是strlen(str) == sizeof str - 1。如果fgets()读取空字符'\0',这会被愚弄。

转角案件:
1.典型的str最多为98 char,然后是'\n''\0'。可以最后 str是99 char然后是'\0'吗? 2.如果#1没问题,那么典型的str可能有99 char然后是'\0'吗?

答案 1 :(得分:1)

当输入太长时,您需要在继续下一次循环迭代之前读取stdin上的剩余字符。

if (fgets(str, size, stdin) == NULL) {
    if (feof(stdin)) {
        return 0;
    else {
        perror("Could not read from stdin");
        exit(1);
    }
}
else if (strchr(str, '\n') == NULL) {
    int c;
    while((c = getc(stdin)) != '\n' && c != EOF);
    fprintf(stderr, "Input too long\n");
}

如果您使用的是OSX或Linux等POSIX系统,则getline函数已从流中读取任意长度的换行符终止字符串。您还可以在线找到此功能的许多免费/开源版本。