【问题标题】:ProbIem with EOF in CC中的EOF问题
【发布时间】:2009-10-25 21:16:20
【问题描述】:

我正在编写一个程序,它应该读取两个可以包含换行符和各种其他字符的字符串。因此,我使用 EOF(Ctrl-Z 或 Ctrl-D)来结束字符串。

这适用于第一个变量,但是对于第二个变量,这似乎是有问题的,因为显然输入缓冲区中卡住了一些东西并且用户没有输入任何内容。

我尝试使用while (getchar() != '\n'); 和几个类似的变体来清理缓冲区,但似乎没有任何帮助。所有的清洗尝试都导致了一个无限循环,不清洗,添加第二个变量是不可能的。

两个变量的字符都在这样的循环中读取:while((c = getchar()) != EOF),这表明它是我卡在缓冲区中的 EOF。或者它是否以其他方式影响程序的行为?我使用的逻辑有问题吗?

在为此苦苦挣扎了几个小时后,我开始有点绝望了。

代码:

#include <stdio.h>
#include <string.h>

int main(void)
{
    int x = 0;
    int c;
    char a[100];
    char b[100];

    printf("Enter a: ");
    while((c = getchar()) != EOF)
    {
        a[x] = c;
        x++;
    }
    a[x] = '\0';
    x = 0;

    /*while (getchar() != '\n'); - the non-working loop*/

    printf("\nEnter b: ");
    while((c = getchar()) != EOF)
    {
        b[x] = c;
        x++;
    }
    b[x] = '\0';

    printf("\n\nResults:\na: %s\n", a);
    printf("b: %s\n", b);

    return(0);
}

【问题讨论】:

  • 在我的 linux 机器上,它运行良好。
  • 它允许您为两个变量输入值?
  • 动态记忆:做一个函数来读取输入。我会让这个功能类似于int read_large_input(char **buf, size_t *len);。我是座右铭“malloc()s 负责free()ing 的功能”的忠实追随者。
  • 嗯,缓冲区是向复杂性迈出的又一步,实际上并不是我想要的,因为即使有一个函数和一个缓冲区,数据也需要存储在某个地方,并且存储大小需要在需要时增加......无论如何,我通过尝试一些东西再次自己做了一些事情,并注意到 realloc 似乎解决了这个问题。性能可能有点糟糕,但至少它不会不足或使用太多内存。刚刚在几行上测试了大约 1000 个字符的程序,完全没有问题,所以它似乎可以工作。感谢您的帮助!

标签: c string newline eof getchar


【解决方案1】:

在您收到来自终端的 EOF 后,您将不会收到任何其他数据。没有办法取消 EOF 输入 - 文件的结尾就是结尾。

因此,您应该定义每个变量在单独的行上输入,并让用户按 Enter 而不是 EOF。您仍然需要检查您是否收到了 eof,因为这意味着用户实际输入了 EOF,而您将看不到任何其他内容 - 在这种情况下,您需要跳出循环并打印错误消息。

【讨论】:

  • 好的,所以不能像我想的那样使用 EOF。谢谢。能够将多行添加到同一个变量中非常重要,有什么明智的方法可以做到这一点吗?
  • 有几种约定: a) 空行(双回车)将终止输入;这应该可以正常工作,除非您的多行输入也应该允许空行。 b) 一些停止字符(通常是“.”,例如在 SMTP 中)将结束输入;假设这在真实文本中不太可能发生。
  • 你可以做一些事情......棘手......并阻止正常的 EOF 行为(EOF 仍然是 EOF,但 Control-D 不需要发送它,因为实例)。不过,这超出了 C 的范围。
【解决方案2】:

EOF 不是 字符 - 它是输入函数返回的一个特殊值,用于指示 条件,即该输入上的“文件结尾”已到达流。正如 Martin v. Löwis 所说,一旦出现“文件结束”情况,就意味着该流上将不再有可用的输入。

产生混淆的原因是:

  • 当“文件”是交互式终端(例如 Ctrl-Z 或 Ctrl-D)时,许多终端类型会识别一个特殊的击键来表示“文件结束”;和
  • EOF 值是getchar() 系列函数可以返回的值之一。

您将需要使用实际字符值来分隔输入 - ASCII nul 字符 '\0' 可能是一个不错的选择,如果它不能在输入本身中显示为有效值。

【讨论】:

  • 是的,我在不知道具体细节的情况下使用了它。谢谢,nul 字符确实很方便,因为它也可以用来终止字符串,但是当我尝试了几种替代方法时,我认为我无法生成它。你是怎么得到它的?
  • Arcthae:这取决于你的系统,但 Ctrl-@ 可以在许多终端上使用。
【解决方案3】:

我在我的 linux 机器上运行代码,结果如下:

Enter a: qwer
asdf<Ctrl-D><Ctrl-D>
Enter b: 123
456<Ctrl-D><Ctrl-D>

Results:
a: qwer
asdf
b: 123
456

需要两个 Ctrl-D,因为终端输入缓冲区不为空。

【讨论】:

  • 哦...这很奇怪。我现在自己在 Linux 上尝试了它,它可以工作(每个变量一个 Ctrl-D)。昨天我在 Windows 上编译,它的工作方式完全不同。
  • 如果最后一行以 结束,则只需要一个
  • 这是这里唯一准确的答案。 – 在 C 级别上,EOF 条件只不过是 read() 返回 0 字节。你仍然可以再次read()
【解决方案4】:

您可以使用空字符 ('\0') 来分隔变量。各种 UNIX 工具(例如 find)能够以这种方式分离它们的输出项,这表明这是一种相当标准的方法。

这样做的另一个好处是您可以将流读入单个缓冲区,然后创建一个 char*s 数组来指向各个字符串,并且每个字符串都将正确地以'\0'-终止而无需您手动更改缓冲区中的任何内容。这意味着更少的内存分配开销,这可能会使您的程序运行得更快,具体取决于您正在读取的变量数量。当然,只有当您需要同时将所有变量保存在内存中时才需要这样做——如果您一次处理一个变量,您就不会获得这种特殊的优势。

【讨论】:

  • 感谢您指出。如何在程序中输入null?
  • 空字符只是一个字节,其值为0,但通常使用转义码\0 输入,您可以在字符串文字和字符文字中使用它(尽管请注意,C 将自动将空字符放在您使用文字定义的任何字符串的末尾,因为它使用它来表示字符串的结尾)。
【解决方案5】:

EOF 根本不可能实现您的尝试。

虽然它在某些方面表现得像一个字符,但 EOF 不是流中的一个字符,而是一个环境定义的宏,表示流的 end。我还没有看到你的代码,但我猜你正在做的是这样的:

while ((c=getchar()) != EOF) {
    // do something
}
while ((c=getchar()) != EOF) {
    // do something else
}

当您第一次键入 EOF 字符以结束第一个字符串时,流将不可撤销地关闭。即流的状态是关闭。

因此,第二个 while 循环的内容永远不会运行。

【讨论】:

  • 现在添加了一些代码。我看到一些程序在使用它,所以我在不知道它的真实性质的情况下尝试在这里使用它。
  • 是的,所以您的代码与我的预期几乎相同。如果这个程序只需要在命令行上运行,我的建议是用空字符分隔字符串。在我的系统 (OS X) 上,这是使用 Ctrl-@ 调用的(即此键盘上的 Ctrl-Shift-2)。
【解决方案6】:

而不是在 EOF 处停止读取输入——不是字符——在 ENTER 处停止。

while((c = getchar()) != '\n')
{
    if (c == EOF) /* oops, something wrong, input terminated too soon! */;
    a[x] = c;
    x++;
}

EOF 是输入终止的信号。您几乎可以保证来自用户的所有输入都以 '\n' 结尾:这是用户键入的最后一个键!!!


编辑:您仍然可以使用 Ctrl-D 和 clearerr() 重置输入流。

#include <stdio.h>

int main(void) {
  char a[100], b[100];
  int c, k;

  printf("Enter a: "); fflush(stdout);
  k = 0;
  while ((k < 100) && ((c = getchar()) != EOF)) {
    a[k++] = c;
  }
  a[k] = 0;

  clearerr(stdin);

  printf("Enter b: "); fflush(stdout);
  k = 0;
  while ((k < 100) && ((c = getchar()) != EOF)) {
    b[k++] = c;
  }
  b[k] = 0;

  printf("a is [%s]; b is [%s]\n", a, b);
  return 0;
}
$ ./a.out
输入一个:两个
行(下一个 ENTER 之后的 Ctrl+D)
输入 b:三
线条
现在(ENTER + Ctrl+D)
a是[两个
行(下一个 ENTER 之后的 Ctrl+D)
]; b是[三
线条
现在(ENTER + Ctrl+D)
]
$

【讨论】:

  • 重点是能够输入带有换行符的输入。就像有人指出的那样,我可以尝试找出我可以使用的其他角色,就像我打算在这里使用 EOF 一样,或者我应该想出一种新的方法来解决这个问题。您的示例 - 在输入处停止也可以工作,但您需要另一个循环,并且仍然需要一种方法来确定用户何时切换到第二个变量,这也允许换行。
  • 谢谢!所以有一种方法可以按照我的想法来做这件事。 :D 重置整个流有点像射击苍蝇吗?
  • getchar() 在文件结束后尝试读取或出现错误时返回EOFclearerr() 告诉程序忽略文件结尾或错误。通常忽略信号是没有意义的:如果文件结束,在忽略 EOF 之后它不会神奇地有新数据,如果出现错误(网络中断,媒体删除,...)clearerr() 不会神奇地纠正错误。对于您的具体问题,这应该可以工作——但用户不能重定向输入。
  • 好的。谢谢,我认为这确实非常适合我的程序。顺便说一句,我看到你在你的代码中测试 k 是否低于 100。最初我打算通过动态内存分配来解决这个问题,但是当我遇到无限循环和与内存相关的崩溃问题时,我将其忽略并切换到 char[100]。我尝试在这里输入我尝试使用内存分配的示例代码,但有点失败,所以我再次编辑了我的问题。 (或者我应该提出一个新问题吗?我对网站及其一般政策有点陌生)
  • 我认为最好提出一个新问题。我的 sn-p 应该真正测试 k &lt; 99 以允许以下终止零。
【解决方案7】:

如何在程序中输入null?

您可以使用以下方法实现 -print0 功能:

putchar(0);

这将打印一个 ASCII nul 字符 '\0' 到 sdtout。

【讨论】:

  • 我的问题格式有点糟糕。我正在从用户的键盘读取输入,并且我正在尝试获取两个可以包含换行符的字符串到两个变量中,并且我需要用户可以结束每个字符串的东西。我最初使用 EOF,有些人告诉我将使用切换到 NULL,但我无法用我的键盘生成那个字符。可能是因为环境(Windows)。尝试了 Ctrl-@ 和其他几乎所有东西,但似乎没有得到 \0。
猜你喜欢
  • 2011-08-11
  • 1970-01-01
  • 2022-07-08
  • 1970-01-01
  • 2012-12-28
  • 2016-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多