这是我的 C 源代码。
当我在 Ubuntu 中构建它时,它开始获取字符,但我不知道如何结束该程序,因为它不能通过输入ENTER或回车来结束。
EOF 是什么意思?我该如何触发它?
Dennis Ritchie 的一本书中也提到了这一来源:
#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);
}
答案1
太长不看
通常,您可以在终端中运行的程序中,在最后一次输入刷新后立即使用CTRL+键来“触发 EOF”。D
EOF 是什么意思?我该如何触发它?
EOF 表示文件结束。
在这种情况下,“触发 EOF”大致意味着“让程序意识到不会再发送任何输入”。
在这种情况下,由于getchar()
如果没有读取任何字符就会返回负数,因此执行终止。
但这不仅适用于您的特定程序,也适用于许多不同的工具。
一般来说,“触发 EOF”可以在最后一次输入刷新之后立即通过CTRL+键来完成(即通过发送一个空输入)。D
例如cat
:
% cat >file # Hit ENTER
foo # Hit ENTER and CTRL+D
%
CTRL当按下+时,底层发生的情况D是,自上次输入刷新以来输入的内容被刷新;当这恰好是一个空输入时,read()
在程序的 STDIN 上调用的系统调用0
将getchar()
返回一个负数(-1
在 GNU C 库中),而这又被解释为 EOF 1。
答案2
总结:EOF 不是字符,它是用于评估输入读取函数的负返回值的宏。可以使用Ctrl+D发送EOT
字符,这将强制函数返回-1
每个程序员都必须阅读手册
让我们参考 Harbison 和 Steele 编写的《CA 参考手册》,1995 年第 4 版,第 317 页:
负整数 EOF 是一个不是“真实字符”编码的值。。。例如 fget(第 15.6 节)返回 EOF当到达文件末尾时,因为没有可读取的“真实字符”。
本质上EOF
不是一个角色,而是一个整数值实现来stdio.h
表示-1
。因此,kos 的答案就这一点而言是正确的,但它与接收“空”输入无关。重要的是要注意,这里的 EOF 充当返回值(的getchar()
)比较,不表示实际字符。man getchar
支持:
返回值
fgetc()、getc() 和 getchar() 将读取的字符作为无符号字符返回,并在文件末尾或出现错误时转换为 int 或 EOF。
如果成功则 gets() 和 fgets() 返回 s,如果出错或到达文件末尾而未读取任何字符则返回 NULL。
如果成功则 ungetc() 返回 c,如果出错则返回 EOF。
考虑while
循环——它的主要目的是重复动作如果括号中的条件为真。再看一下:
while ((c = getchar ()) != EOF)
它基本上是说如果 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
ASCII 代码和 EOT
尽管 EOF 字符不是实际字符,但是存在一个EOT
(传输结束)字符,其 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
因此,我们可以说它确实存在,但不可打印
边注
我们常常忘记,过去的计算机并不像现在这样多功能——设计师必须利用每个可用的键盘键。因此,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
棺材上又钉上一颗钉子
有时会尝试用如下代码来证明 EOF 是一个字符:
#include <stdio.h>
int main(void)
{
printf("%c", EOF);
return 0;
}
问题在于 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
重点到底是什么?重点是 EOF 定义为 -1,但是char 数据类型可以打印整数值。
好的。。。那么如果我们尝试将字符打印为字符串会怎么样?
#include <stdio.h>
int main(void)
{
printf("%s", EOF);
return 0;
}
显然是一个错误,但尽管如此,错误会告诉我们一些有趣的事情:
skolodya@ubuntu:$ gcc EOF.c -o EOF
EOF.c: 在函数‘main’中:EOF.c:4:5: 警告:格式‘%s’需要类型为‘char *’的参数,但参数 2 的类型为 'int' [-Wformat=] printf("%s",EOF);
十六进制值
将 EOF 打印为十六进制值将得到FFFFFFFF
,一个 16 位(8 字节)的值,即 的二进制补码-1
。
#include <stdio.h>
int main(void)
{
printf("This is EOF: %X\n", EOF);
printf("This is Z: %X\n",'Z');
return 0;
}
输出:
DIR:/xieerqi
skolodya@ubuntu:$ ./EOF
This is EOF: FFFFFFFF
This is Z: 5A
以下代码还发生了另一件奇怪的事情:
#include <stdio.h>
int main(void)
{
char c;
if (c = getchar())
printf ("%x",c);
return 0;
}
如果按下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
参考其他语言
请注意,其他语言可以避免这种混淆,因为它们对函数退出状态进行评估,而不是将其与宏进行比较。如何在 Java 中读取文件?
File inputFile = new File (filename);
Scanner readFile = new Scanner(inputFile);
while (readFile.hasNext())
{ //more code bellow }
那 Python 怎么样?
with open("/etc/passwd") as file:
for line in file:
print line
答案3
末梢血代表文件结尾。虽然我不知道如何触发以下符号,但你可以通过管道文件来运行以下程序,它会发送末梢血最后信号:
echo "Some sample text" | ./a.out
a.out
你的编译源代码在哪里