我已经做到了这一点,知道EOF
是在文本文件末尾自动插入的特殊字符,以指示其结束。但我现在觉得有必要对此作进一步澄清。我在谷歌和维基百科上查了EOF
,但他们无法回答以下问题,也没有确切的Stack Overflow链接。所以请帮助我:
-
我的书说二进制模式文件从文件的目录条目中存在的字符数跟踪文件的结束。(与文本文件相反,文本文件有一个特殊的EOF字符来标记结束)。那么
EOF
在二进制文件背景下的故事是什么呢?我很困惑,因为在以下程序中,我成功地使用!=EOF
比较,同时从二进制模式的.exe
文件读取:#include<stdio.h> #include<stdlib.h> int main() { int ch; FILE *fp1,*fp2; fp1=fopen("source.exe","rb"); fp2=fopen("dest.exe","wb"); if(fp1==NULL||fp2==NULL) { printf("Error opening files"); exit(-1); } while((ch=getc(fp1))!=EOF) putc(ch,fp2); fclose(fp1); fclose(fp2); }
-
有趣的是,EOF
是一个特殊的"字符"吗?或者是像维基百科所说的条件,计算机知道何时返回特定值的条件,如-1
(我的计算机上的EOF
)?这种"条件"的例子是当字符读取函数完成读取所有字符时,或者当字符/字符串I/O函数在读/写中遇到错误时?EOF
的Stack Overflow标签混合了EOF
的两个定义。EOF
的标签说"在编程领域,EOF是一个字节序列(或一个字符),它表明在此之后没有更多的内容。",而在"关于"一节中还说"文件结束(通常缩写为EOF)是计算机操作系统中不能从数据源读取更多数据的一种情况。数据源通常称为文件或流。"
但是我有一种强烈的感觉,EOF
不会是一个字符,因为当它在I/o期间遇到错误时,其他函数似乎都会返回它。
C提供给您的各种EOF指示器不一定与文件系统如何标记文件结束有任何关系。
大多数现代文件系统都知道一个文件的长度,因为它们把它记录在某个地方,与文件的内容分开。读取文件的例程跟踪您正在读取的位置,并在您到达末尾时停止。C库例程生成一个EOF值返回给你;它们没有返回实际存在于文件中的值。
注意C库例程返回的EOF实际上不是一个字符。C库例程通常返回int
,并且int
是字符值或EOF。例如,在一个实现中,字符的值可能在0到255之间,EOF的值可能在−1之间。当库例程遇到文件末尾时,它实际上没有看到−1字符,因为没有这样的字符。相反,底层系统例程告诉它已经到达文件的末尾,并返回- 1作为响应。
旧的和粗糙的文件系统可能在文件中有一个值来标记文件的结束。出于各种原因,这通常是不可取的。在其最简单的实现中,不可能在文件中存储任意数据,因为不能将文件结束标记存储为数据。但是,可以有这样一种实现,其中文件中的原始数据包含指示文件结束的内容,但是在读取或写入时对数据进行转换,以便可以存储任意数据。(例如,通过"引用"文件结束标记。)
在某些情况下,像文件结束标记这样的东西也出现在流中。这在从终端(或伪终端或类似终端的设备)读取时很常见。在Windows上,按control-Z表示用户已经完成输入,它的处理方式类似于到达文件结束符。这并不意味着control-Z是一个EOF。从终端读取的软件看到control-Z,将其视为文件结束,并返回可能与control-Z不同的文件结束指示。在Unix上,control-D通常是一个类似的标记输入结束的哨兵。
这样你就清楚了。
基本上,EOF只是一个宏,它有一个预定义的值,表示I/O函数的错误代码,表示没有更多的数据要读取。
该文件实际上不包含EOF。EOF不是一个排序字符——记住一个字节可以在0到255之间,所以如果一个文件可以包含-1就没有意义了。EOF是来自您正在使用的操作系统的一个信号,它表示已经到达文件的末尾。注意getc()如何返回一个int
——这样它就可以返回-1来告诉您流已经到达文件的末尾。
EOF信号对二进制文件和文本文件的处理是相同的-二进制和文本流的实际定义在操作系统之间是不同的(例如在*nix上二进制和文本模式是一样的)。无论哪种方式,如上所述,它都不是文件本身的一部分。操作系统将它传递给getc()来告诉程序流的结尾已经到达。
From From GNU C库:
该宏是一个整数值,由多个窄流函数返回,用于指示文件结束条件或其他错误情况。在GNU C库中,EOF为-1。在其他库中,它的值可以是其他一些负数。
EOF
不是字符。在这种情况下,它是-1,从技术上讲,它不是一个字符(如果你想要非常精确,它可能是一个字符,但这与本讨论无关)。EOF
,只是为了明确是"文件结束"。当您读取文件时,您需要知道何时停止,否则,如果您试图读取超过文件末尾,根据环境可能会发生许多事情。
因此,设计了一个宏来表示在读取文件的过程中到达了文件结束(End of File),即EOF
。对于getc
,这是有效的,因为它返回int
而不是char
,所以有额外的空间返回char
以外的东西来信号EOF
。其他I/O调用可能以不同的方式通知EOF
,例如抛出异常。
作为一个有趣的点,在DOS(也许仍然在Windows上?)中,一个实际的,物理字符^Z
被放置在文件的末尾,以表示它的结束。所以,在DOS上,实际上有一个EOF
字符。Unix从来没有这样的东西。
如果你研究二进制文件的结构,找到它的EOF是很有可能的。
不,您不需要操作系统知道可执行EOF的EOF。
几乎每种类型的可执行文件都有一个Page Zero,它描述了操作系统在将代码加载到内存时可能需要的基本信息,并作为可执行文件的第一页存储。
让我们以MZ可执行文件为例。https://wiki.osdev.org/MZ
在偏移量2处,我们有完整/部分页面的总数,紧接着在偏移量4处,我们有最后一页的字节数。操作系统通常使用此信息将代码安全地加载到内存中,但是您可以使用它来计算二进制文件的EOF。
算法:
1. Start
2. Parse the parameter and instantiate the file pointer as per your requirement.
3. Load the first page (zero) in a (char) buffer of default size of page zero and print it.
4. Get the value at *((short int*)(&buffer+2)) and store it in a loop variable called (short int) i.
5. Get the value at *((short int*)(&buffer+4)) and store it in a variable called (short int) l.
6. i--
7. Load and print (or do whatever you wanted to do) 'size of page' characters into a buffer until i equals zero.
8. Once the loop has finished executing just load `l` bytes into that buffer and again perform whatever you wanted to
9. Stop
如果你正在设计自己的二进制文件格式,那么考虑在文件的开头添加一些元数据,或者添加一个特殊的字符或单词来表示文件的结尾。
很有可能操作系统通过简单的数学和分析元数据从这里加载文件的大小,尽管看起来操作系统已经将它与其他预期存储的信息一起存储在某个地方(抽象以减少冗余)。