【问题标题】:Read size-limited input line ignoring the tail part读取大小有限的输入行,忽略尾部
【发布时间】:2015-11-02 19:15:08
【问题描述】:

是否有一个库函数可以从标准输入读取一行输入并满足以下要求?

  1. 我有一个特定大小的有限静态缓冲区(大小可能是一个已知常数)。
  2. 不允许动态分配。所以不能使用getline()之类的库函数。
  3. 对于长度超过缓冲区大小的行,该行的未读尾部将被忽略。

我读取一行的解决方案是使用fgets 和一个循环来读取并忽略尾部。代码如下

char buffer[80], tail_buffer[80];
char * line, * tail;
line = tail = fgets(buffer, 80, stdin);
/* Read the tail part to ignore it */
while (tail != NULL && tail[strlen(tail)-1] != '\n')
{
    tail = fgets(tail_buffer, 80, stdin);
}
/* Use 'line' as needed */

【问题讨论】:

  • 我不会说您的解决方案真的很优雅,原因之一是您使用的是while(!feof(stdin)),即wrong
  • @Linus !feof(stdin) 有什么问题?我使用它是因为结束字符可能不是 EOF 上的 '\n'。但这可能是不必要的,因为 fgets 的下一次调用无论如何都会返回 NULL。
  • @jayant 这里不需要代码审查。我只需要了解是否有类似于 getline() 的库函数通过截断尾部将完整的行读取到有限的缓冲区中。
  • “我正在努力寻找更好的解决方案”。那是为了代码审查。

标签: c scanf fgets


【解决方案1】:

另一种解决方案是使用scanf 读取直到找到换行符,然后使用getchar 读取换行符。

char buffer[80];
if ( fgets( buffer, sizeof buffer, stdin ) != NULL )
{
    if ( strchr( buffer, '\n' ) == NULL )   // if the buffer does not contain the newline
    {
        scanf( "%*[^\n]" );                 // read up to the newline
        getchar();                          // read the newline
    }
}

【讨论】:

  • 这听起来像是一个可行的解决方案。也许您可以尝试仅使用 scanf 和 getchar 而不是将其与 fget 混合使用,如scanf("%79[^\n]%*[^\n]", buffer); getchar();
  • 极端情况:strchr( buffer, '\n' ) 仅读取 buffer'\0',如果 fgets() 读取空字符,则可能在 '\n' 之前。在用户输入中发现空字符很不寻常,但这是黑客的破解。
  • 如果我们通过scanf本身读取缓冲区,我认为不需要检查'\n'来确定是否读取尾部。这是因为,即使没有尾部(所有内容都读入缓冲区),%*[^\n] 也只会解析为空。
  • @chux 你说得对,输入中有一个空字符会导致一行被跳过,但没有其他不良影响。
【解决方案2】:

查看scanf的文档后,我自己找到了解决方案。

char buffer[80];
scanf(" %79[^\n]%*[^\n]", buffer);

编辑:使用来自@chux 的 cmets,这在解析空白行和初始空格方面有一些限制,并且使用来自 @user3386109 的解决方案,我将其增强如下以扫描所有行直到 EOF。

char buffer[80] = "";

while (scanf("%79[^\n]%*[^\n]", buffer) != EOF)
{
    /* Process the line in buffer */

    if (feof(stdin)) break;
    getchar(); /* Remove end of line */
    buffer[0] = 0;
}

【讨论】:

  • %80 应该是 %79。也丢弃了开头的空格。
  • " %79[^\n]..." 将忽略一行中的前导空格,因此像"\t123" 这样的输入会忽略前导'\t'。忽略帖子中未提及的前导空格。
  • 我认为可能是 scanf 之一(或一句话)想要的操作是不可能的。
  • 使用新的char buffer[80]; scanf("%79[^\n]%*[^\n]", buffer), getchar(),如果第一个char'\n',则buffer 中不会保存任何内容,并且在后续代码使用未初始化的缓冲区时可能会导致问题。健壮的代码不使用scanf()
  • 最新的看起来不错——除了 1 个小洞——如果你在乎的话——它很模糊。代码没有使用scanf() 返回的原因。 scanf() 在 2 个条件下返回 EOF:文件结束和输入错误。由于输入错误,buffer 的值不确定。因此,当返回EOF 并且代码不检查feof() / ferror() 时,任何使用buffer[0]=0 之类的值预加载buffer 的尝试都是没有意义的。当输入函数返回 EOF 时,IOW 不使用 buffer
【解决方案3】:

fgets() 有一些极端情况,无法在 OP 目标的完整解决方案中使用它。
只需使用fgetc() 循环即可。

// Return count of `char` read - not including potential \n.
int read_line(char *dest, int size) {
  int i = 0;
  if (size > 0) {
    size--;
    int ch;
    while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
      if (i < size) {
        dest[i++] = ch;
      }
    }
    dest[i] = '\0';
    if (ch == EOF && i == 0) return EOF;
    }
  return i;
}

fgets() 的强制使用看起来像

bool was_there_extra(char *buf, size_t size) {
  char *lf = strchr(buf, '\n');
  if (lf) {
    *lf = '\0';  // optional: lop off potential trailing \n
    return false;
  }
  int ch;
  bool extra = false;
  while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
    extra = true;
  }
  return extra;
}

while (fgets(buf, sizeof buf, stdin)) {
  if (was_there_extra(buf, sizeof buf)) ...
  else ...
}

如果代码读取'\0',这种方法确实会被愚弄。

【讨论】:

  • 我不是在缓冲区中寻找 \n。但需要阅读并忽略该行剩余的未读部分。
  • 欣赏另一个使用 fgetc() 的解决方案,它不需要 tail_buffer。
  • @user1969104 需要明确的是,这里的答案都没有使用int 以外的尾部缓冲区。 read_line() 不是读尾缓冲区,是读行。
  • 我明白了,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-05
  • 2023-03-20
  • 2015-08-13
相关资源
最近更新 更多