什么是EOF以及如何触发它?

sta*_*mer 14 command-line c

这是我的 C 源代码。

当我在 Ubuntu 中构建它时,它开始获取字符,但我不知道如何结束程序,因为它不会以输入ENTER或回车结束。

EOF是什么意思?我怎样才能触发它?

这个来源也来自丹尼斯·里奇的一本书:

#include <stdio.h>
    /* count digits, white space, others */
main ()
{
  int c, i, nwhite, nother;
  int ndigit[10];
  nwhite = nother = 0;
  for (i = 0; i < 10; ++i)
    ndigit[i] = 0;
  while ((c = getchar ()) != EOF)
    if (c >= '0' && c <= '9')
      ++ndigit[c - '0'];
    else if (c == ' ' || c == '\n' || c == '\t')
      ++nwhite;
    else
      ++nother;
  printf ("digits =");
  for (i = 0; i < 10; ++i)
    printf (" %d", ndigit[i]);
  printf (", white space = %d, other = %d\n", nwhite, nother);
}
Run Code Online (Sandbox Code Playgroud)

kos*_*kos 28

Tl;博士

您通常可以在最后一次输入刷新后立即使用CTRL+D键在终端中运行的程序中“触发 EOF” 。


EOF是什么意思?我怎样才能触发它?

EOF 表示文件结束。

在这种情况下,“触发 EOF”大致意味着“让程序意识到不会再发送输入”。

在这种情况下,由于getchar()如果没有读取字符将返回一个负数,因此执行终止。

但这不仅适用于您的特定程序,还适用于许多不同的工具。

通常,“触发 EOF”可以在最后一次输入刷新后立即使用CTRL+D键完成(即通过发送空输入)。

例如cat

% cat >file # Hit ENTER
foo # Hit ENTER and CTRL+D
% 
Run Code Online (Sandbox Code Playgroud)

当点击CTRL+时,引擎盖下发生的事情D是自上次输入刷新以来输入的输入被刷新;当这恰好是一个空输入read()时,程序的 STDIN 上调用的系统调用返回0getchar()返回一个负数(-1在 GNU C 库中),这又被解释为 EOF 1


1 - /sf/answers/106132421/

  • 编译有效,因为逗号分隔不受在同一行中的约束。除此之外,对EOF的很好的解释:) (2认同)

Ser*_*nyy 6

TL;DR:EOF 不是字符,它是用于评估输入读取函数的负返回值的宏。可以使用Ctrl+D发送EOT字符,这将强制函数返回-1

每个程序员都必须 RTFM

让我们参考 Harbison 和 Steele,第 4 版的“CA 参考手册”。从 1995 年起,第 317 页:

负整数 EOF 是一个不是“真实字符”编码的值。. . 例如 fget(第 15.6 节)在文件结束时返回 EOF,因为没有要读取的“真实字符”。

本质EOF上不是一个字符,而是一个实现的整数值stdio.h来表示-1. 因此,就目前而言,kos 的答案是正确的,但这与接收“空”输入无关。重要的一点是,这里 EOF 用作(的)比较的返回值getchar(),而不是表示实际字符。该man getchar支架是:

返回值

fgetc()、getc() 和 getchar() 在文件末尾或错误时返回作为无符号字符读取的字符转换为 int 或 EOF。

get() 和 fgets() 在成功时返回 s,在错误时返回 NULL,或者在没有读取任何字符的情况下出现文件结尾。

ungetc() 成功时返回 c ,错误时返回 EOF 。

考虑while循环——它的主要目的是在括号中的条件为真时重复操作。再看:

while ((c = getchar ()) != EOF)
Run Code Online (Sandbox Code Playgroud)

它基本上是说如果 c = getchar()返回成功的代码(0或更高版本;顺便说一下,这是很常见的事情,尝试运行成功的命令,echo $?然后失败echo $?并查看它们返回的数字),请继续执行操作。因此,如果我们成功获取字符并分配给 C ,则返回状态代码为 0,失败为 -1。EOF被定义为-1。因此,当条件-1 == -1发生时,循环停止。什么时候会发生?当没有更多的字符可以获取时,c = getchar()失败时。你可以写while ((c = getchar ()) != -1),它仍然可以工作

另外,让我们回到实际的代码,这是摘录自 stdio.h

/* End of file character.
   Some things throughout the library rely on this being -1.  */
#ifndef EOF
# define EOF (-1)
#endif
Run Code Online (Sandbox Code Playgroud)

ASCII 码和 EOT

虽然EOF字符不是真正的字符,但是存在一个EOT(End of Transmission)字符,它的ASCII十进制值为04;它链接到Ctrl+D快捷方式(也表示为元字符^D)。当计算机用于控制电话连接时,传输结束字符用于表示关闭数据流,因此称为“传输结束”。

所以可以像这样将 ascii 值发送给程序,注意$'\04'哪个是 EOT:

skolodya@ubuntu:$ ./a.out  <<< "a,b,c $'\04'"                                  
digits = 1 0 0 0 1 0 0 0 0 0, white space = 2, other = 9
Run Code Online (Sandbox Code Playgroud)

因此,我们可以说它确实存在,但它是不可打印的

边注

我们常常忘记,过去的计算机并不是那么通用——设计师必须利用每个可用的键盘键。因此,EOT使用 CtrlD发送字符仍然是“发送字符”,与键入大写 A、ShiftA 不同,您仍然使用可用键向计算机输入。因此,从某种意义上说,EOT 是一个真正的字符,它确实来自用户,它可以被计算机读取(虽然不可打印,人类不可见),它存在于计算机内存中

字节指挥官的评论

如果您尝试从 /dev/null 读取,那也应该返回一个 EOF,对吗?或者我到那里去做什么?

是的,完全正确,因为/dev/null里面没有要读取的实际字符,因此它c = getchar()会返回-1代码,并且程序会立即退出。再次命令不返回 EOF。EOF 只是一个等于 -1 的常量变量,我们用它来比较 getchar 函数的返回码EOF不作为字符存在,它只是内部的一个静态值stdio.h

演示:

# cat /dev/null shows there's no readable chars
DIR:/xieerqi
skolodya@ubuntu:$ cat /dev/null | cat -A        

# Bellow is simple program that will open /dev/null for reading. Note the use of literal -1                                   
   DIR:/xieerqi
skolodya@ubuntu:$ cat readNull.c                                               
#include<stdio.h>

void main()
{
   char c;
    FILE *file;
    file = fopen("/dev/null", "r");

    if (file) 
    {
    printf ("Before while loop\n");
        while ((c = getc(file)) != -1)
            putchar(c);
    printf("After while loop\n"); 
    fclose(file);
    }
}

DIR:/xieerqi
skolodya@ubuntu:$ gcc readNull.c -o readNull                                   

DIR:/xieerqi
skolodya@ubuntu:$ ./readNull
Before while loop
After while loop
Run Code Online (Sandbox Code Playgroud)

棺材上的另一个钉子

有时试图证明 EOF 是一个字符,其代码如下:

#include <stdio.h>
int main(void)
{
    printf("%c", EOF);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

问题在于 char 数据类型可以是有符号或无符号值。此外,它们是最小的可寻址数据类型,这使得它们在内存有限的微控制器中非常有用。因此,int foo = 25;在具有小内存char foo = 25;或类似的东西的微控制器中看到它是常见的,而不是声明。此外,字符可能有符号或无符号

可以使用这样的程序验证以字节为单位的大小:

#include <stdio.h>
int main(void)
{
    printf("Size of int: %lu\n",sizeof(int));
    printf("Sieze of char: %lu\n",sizeof(char));
    //printf("%s", EOF);
    return 0;
}

skolodya@ubuntu:$ ./EOF                                                        
Size of int: 4
Sieze of char: 1
Run Code Online (Sandbox Code Playgroud)

重点是什么?关键是 EOF 定义为 -1,但char 数据类型可以打印整数值

好的 。. .那么如果我们尝试将 char 打印为 string 呢?

#include <stdio.h>
int main(void)
{
    printf("%s", EOF);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

显然是一个错误,但无论如何,错误会告诉我们一些有趣的事情:

skolodya@ubuntu:$ gcc EOF.c -o EOF
EOF.c:在函数“main”中:EOF.c:4:5:警告:格式“%s”需要“char *”类型的参数但参数 2 有type 'int' [-Wformat=] printf("%s", EOF);

十六进制值

将 EOF 打印为十六进制值给出FFFFFFFF16 位(8 字节)值,一个-1.

#include <stdio.h>
int main(void)
{
    printf("This is EOF: %X\n", EOF);
    printf("This is Z: %X\n",'Z');
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

DIR:/xieerqi
skolodya@ubuntu:$ ./EOF                                                        
This is EOF: FFFFFFFF
This is Z: 5A
Run Code Online (Sandbox Code Playgroud)

另一个奇怪的事情发生在以下代码中:

#include <stdio.h>
int main(void)
{
   char c;
   if (c = getchar())
    printf ("%x",c);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果按下Shift+ A,我们会得到十六进制值 41 ,显然与 ASCII 表中的相同。但是对于Ctrl+ D,我们ffffffff再次拥有 -getchar()存储在的返回值c

DIR:/xieerqi
skolodya@ubuntu:$ gcc  EOF.c -o ASDF.asdf                                      

DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
A
41
DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
ffffffff
Run Code Online (Sandbox Code Playgroud)

参考其他语言

请注意,其他语言避免了这种混淆,因为它们在评估函数退出状态时进行操作,而不是将其与宏进行比较。如何在 Java 中读取文件?

    File inputFile  = new File (filename);
    Scanner readFile = new Scanner(inputFile);
    while (readFile.hasNext())
        { //more code bellow  }
Run Code Online (Sandbox Code Playgroud)

蟒蛇呢?

with open("/etc/passwd") as file:
     for line in file:
          print line
Run Code Online (Sandbox Code Playgroud)