【问题标题】:fgets from stdin with unpredictable input size来自标准输入的 fgets 具有不可预测的输入大小
【发布时间】:2017-12-08 16:52:26
【问题描述】:

我正在尝试从stdin 中读取一行,但我不知道如何正确处理输入大小至少等于限制的情况。示例代码:

void myfun() {
    char buf[5];
    char somethingElse;

    printf("\nInsert string (max 4 characters): ");
    fgets (buf, 5, stdin);

    ...

    printf("\nInsert char: ");
    somethingElse = getchar();
}

现在,用户可以做三件事:

  1. 输入少于 4 个字符(加上换行符):在这种情况下,stdin 中没有任何内容,随后的 getchar() 正确等待用户输入;
  2. 正好输入 4 个字符(加上换行符):在这种情况下,stdin 中会留下一个换行符,随后的 getchar() 会读取它;
  3. 输入超过 4 个字符(加上换行符):在这种情况下,stdin 中至少还有一个字符,随后的 getchar() 读取它,至少留下一个换行符。

案例 23 需要使用类似 while(getchar() != '\n') 的方式清空 stdin,而案例 1 不需要任何额外的行动。据我通过阅读类似问题的答案和 c-faq 了解到,没有标准/便携的方法可以知道实际场景是否是 1 中描述的场景。

我做得好吗?或者实际上有 一种可移植的方式来做到这一点?或者可能是完全不同的方法?

【问题讨论】:

  • 检查buf[]是否包含换行符?
  • 最多 3 个字符,而不是 4 个,因为 \n 也会进入缓冲区。
  • 你是对的@MichaelWalz,应该警告用户。

标签: c stdin fgets


【解决方案1】:

如果有空间,fgets 函数会将换行符存储在缓冲区中。因此,如果字符串中的最后一个字符不是换行符,您就知道需要刷新缓冲区。

fgets (buf, 5, stdin);
if (strrchr(buf, '\n') == NULL) {
    // flush buffer
    int c;
    while ((c = getchar()) != '\n') && (c != EOF));
}

【讨论】:

  • 其实就是这么简单,惭愧自己没有想到。
【解决方案2】:

如果假设永远不会读取 空字符 '\0',则 @dbush 答案将起作用。

如果读取了空字符,则strrchr(buf, '\n') 找不到任何可能已读取的'\n'

代码可以预先设置缓冲区以查看最后是否读取了'\n'

buf[sizeof buf - 2] = '\n';
if (fgets (buf, sizeof buf, stdin)) {
  if (strrchr(buf, '\n') == NULL) {
    // incomplete line read.  (the usual detection)
  } else if (buf[sizeof buf - 2] != '\n') {
    // incomplete line read with prior null character  (see below note).
  }
}

然而,C 标准并没有指定在 buf[] 中读取的数据之后的数据保持不变,用某种模式预填充缓冲区不足以检测是否为 null 字符 '\0'已阅读。

是一种可移植的方式吗?

最便携的方法是使用重复调用fgetc() 或类似的方法而不是fgets()

也许是完全不同的方法?

我推荐fgetc()或通用但非C标准的getline()


另一种选择:使用scanf("%4[^\n]%n", buf, &n):它非常麻烦,但可移植的方式是可能的。它会跟踪在'\n' 之前读取的字符数,即使有些是空字符

  int n = 0;
  cnt = scanf("%4[^\n]%n", buf, &n);  // Cumbersome to get that 4 here.
  // Lots of TBD code now needed:
  // Handle if cnt != 1 (\n to be read or EOF condition)
  // Handle if n == sizeof buf - 1, (0 or more to read)
  // Handle if n < sizeof buf - 1, (done, now get \n)
  // A \n may still need to be consumed
  // Handle EOF conditions

【讨论】:

  • 我放弃了“重复fgetch()”选项,但我不认为用户输入了空字符。这不太可能发生,但我想你永远不会太安全。事实上,C 标准不保证 \0 之后的数据保持不变,实际上使 fgetc() 选项恢复了活力。好答案!
  • @pimple “这不太可能发生。”我同意 - 除非该用户是黑客。大部分(早期)C 都基于受信任的用户。这种模式在未来是有风险的。根据需要编写代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
  • 1970-01-01
  • 2017-07-07
  • 1970-01-01
相关资源
最近更新 更多