这是我的 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
您通常可以在最后一次输入刷新后立即使用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 上调用的系统调用返回0,getchar()返回一个负数(-1在 GNU C 库中),这又被解释为 EOF 1。
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)