标准输入流(stdin)的文件结束标志(EOF)

18

stdin 是否有 EOF(结束符)?例如,如果我使用 freadread 从 stdin 开始读取,那么下面的循环是否会结束?

while ((c = read(0, buffer, BUFSIZ)) > 0) {
    .
    .
    .
}

如果这个问题的答案是否定的,那么有没有办法向标准输入中添加EOF?

6个回答

30

谈到stdin中的EOF:当您从文件重定向输入时,例如:

program <input.txt
文件已经有一个EOF,所以这不是问题。在控制台中,您可以模拟EOF标志。在UNIX系统中,它是Ctrl + D,在Windows中是Ctrl + Z。当您在控制台中键入此内容时,程序将表现得像刚刚到达了输入文件的结尾。根据OP的问题:那么这是否意味着stdin没有EOF,我们必须使用Ctrl + Z或Ctrl + D手动插入它们?实际上-是的。可以将stdin(未重定向,但从控制台获取)视为无限文件--没有人可以告诉它何时结束。其中输入为stdin的输入文件必须通过Ctrl + D或Ctrl + Z明确告知其输入文件的结尾。

1
正如我刚学到的那样,<kbd>Ctrl</kbd>+<kbd>D</kbd>并不是“发送EOF”,而是发送EOT。在没有输入(或在所有输入已经发送后)的情况下发送EOT会使stdin具有EOF。但是,当您输入“ABC”,然后按<kbd>Ctrl</kbd>+<kbd>D</kbd>时,它只会将缓冲区发送到进程,而不是“信号”EOF。 - ljrk
如果你将输入重定向到 /dev/null,它会立即 EOF。 - dashesy

4

我从未在Windows中编写C程序,因此无法告诉你,但在Bash中,当您键入数据结尾时(Ctrl + D),程序将收到EOF信号。


1
在 MS-DOS 中,Ctrl+Z 键也会表示 EOF 条件。(但在 *nix 系统上不是这样)。 - Thomas Matthews
是的,基本上这是向流发送EOF。 - LukeN
@LukeN 实际上它只是关闭了流,没有实际的EOF字符。 - Bart van Heukelom
@Bart van Heukelom,在输入Ctrl+D后,如果我想以后从stdin读取更多的输入,我该怎么办?这是可能的吗? - Ravi Gupta
3
如果你正在使用stdio,你可以调用 clearerr(stdin); 来清除EOF状态,这样就可以继续读取了。(如果stdin是一个自EOF发生后大小没有改变的文件,那么在你尝试读取时,你仍然会得到新的EOF状态)。如果你使用底层的read()函数,当终端处于EOF状态时,它会返回0,但如果你尝试再次读取,它将阻塞等待输入。 - R.. GitHub STOP HELPING ICE

2
while ((c = read(0, buffer, BUFSIZ)) > 0) { 

您没有说明 c 的类型,但使用该名称意味着它是 char 。请注意,iosteams的EOF值为(int) -1。将其存储到无符号字符中将得到一个值为255,这将不匹配EOF。


好的... cint类型...我只是举了一个例子...现在回到原始问题,根据你的观点,答案是什么? - Ravi Gupta

0

测试 EOF 的方法是检查 fread 的返回值,然后使用 feof:

while( fread( ... ) ) {    // loop so long as fread does not return zero
    // do something
}

if ( feof( stdin ) ) {
   // read failed because of EOF
}
else {
   // some other error
}

@Ravi 这不应该发生。 - anon
为什么?...你的意思是说while循环会在有限次迭代后返回...如果是这样,那么是在多少次之后返回? - Ravi Gupta
@Ravi fread() 试图读取数据 - 如果没有要读取的内容,它会返回零。 因此,上面的循环将读取标准输入或文件,直到遇到EOF。 - anon
@Neil Butterworth 实际上,我填写了其他细节以使其成为完整的程序。以下是整个代码。`#include <stdio.h> #include <stdlib.h> #include <string.h>#define SIZE 65536int main() { char buffer[SIZE]; int c; while ((c = fread(buffer, 1, SIZE, stdin)) > 0) { printf("Inside while\n"); }if ( feof( stdin ) ) { printf("read failed because of EOF\n"); } else { printf("some other error\n");}return 0;} ` - Ravi Gupta
@Neil:确实。我错误地假设fread()返回1个读取的字节(就像Java流上的read()一样)。 - Bart van Heukelom
显示剩余2条评论

0

你的解决方案被标记为C++,所以这里提供一些关于C++的内容。

std::string lols;
while(!(std::cin >> lols).eof()) { // This loop will end when EOF is reached
    // Process string
}

循环会结束吗?这就是我在问的,我不是在问如何以不同的方式编写相同的代码。 - Ravi Gupta
3
你过于复杂化了它,导致出现了错误。如果读取失败是由于其他错误引起的,则流将被标记为坏的并且不再读取,但是EOF将不会被达到,因此会一直循环下去。更简单和正确的循环方式应该是:'while( std::cin >> lols) {}' - Martin York
@Martin York:我最初没有使用EOF,但OP的评论表明他确实需要关于发生了什么的提示。@Ravi Gupta:它会一直运行直到EOF。这就是为什么我加入了EOF函数检查的原因。以便让代码执行直到EOF变得非常明显。并且有一个注释在那里,告诉你循环将在达到EOF时结束。我的意思是,这段代码相当容易理解。 - Puppy

0

首先,getchar() 实际上是 getc(stdin),所以 getc(FILE) 可以从中读取更多信息。getchar() 从输入流或 stdin 中获取最后一个未处理的字符,按下回车键是 '\n'。如果 stdin 为空,则 getchar() 强制暂停以获取输入。

例如,在程序中我首先调用 getchar(),stdin 为空,因此它会暂停等待输入。如果我输入 ab'\n',第一个 getchar() 将获得 'a' 的 ASCII 码 97。下一次调用 getchar() 时,我将获得 b,然后再次调用 getchar() 将获得 '\n'。

为了证明这一点,请编写以下代码。

    int choice;

    do
   {
      cout << "Enter input:" ;
      choice = getchar(); 
      cout << "Last getchar(): " << choice << ":" << (char) choice ; 
       if( choice == '0' || choice == EOF)
         {
           cout << "Exited loop" << endl;     // EOF is ctrl+z in windows 
           break;
         }

   }while(true);

我相信 stdin 是一个全局变量,因此在调用 getchar() 或类似的函数清除流之前,字符将保留在那里,如果您在其他地方使用 getchar(),这可能会导致以后出现错误。正如其他人提到的,您可以使用 gets(char[]) 将所有字符放入字符数组中直到换行符为止。问题是您需要比输入更大的 char[],否则会出现错误。好处是 gets(char[]) 会清除 stdin,因此您可以制作一个虚拟缓冲区来清除 stdin 或处理它。

希望这对你有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接