EOF - 学习记录 2020/12/24

研二


EOF

今天想用C写一个复制函数的程序,为马上要写的传输文件的程序做准备。但在读取文件的时候遇到了一个很诡异的问题。首先放出代码:

#include <stdio.h>
#include <fcntl.h>
#include <time.h>

#define MAXLEN 256

int main(int argc, unsigned char *argv[])
{
	int f1, f2, n, i, read_total = 0, write_total = 0;
	unsigned char buf[MAXLEN] = {0};
	time_t start_time, end_time;
	
	if (argc != 3) {
		printf("Format: cp argv[1] to argv[2]\n");
		return -1;
	}
	
	if ((f1 = open(argv[1], O_RDONLY, 0)) < 0) {
		printf("Failed opening file %s\n", argv[1]);
		return -2;
	}
	
	if ((f2 = creat(argv[2], 0666)) < 0) {
			printf("Filed creating file %s\n", argv[2]);
			return -3;
	}
	
	start_time = time(NULL);
	while ((n = read(f1, buf, MAXLEN)) > 0) {
		read_total += n;
		printf("Write bytes: %d\n", n);
		for (i = 0; i < n; i++)
			printf("%02X ", buf[i]);
		printf("\nEnd of write bytes\n");
		printf("Write bytes: %d\n", n);
		
		if (write(f2, buf, n) != n) {
			printf("Failed writing file %s\n", argv[2]);
			return -4;
		}
		write_total += n;
		for (i = 0; i < n; i++)
			printf("%02X ", buf[i]);
		printf("\nEnd of read bytes\n");
		printf("Write bytes: %d\n", n);
		
		if (read_total != write_total) {
			break;
		}
	}
	end_time = time(NULL);
	
	printf("used time: %d\n", end_time - start_time);
	printf("write bytes: %d, read bytes: %d\n", write_total, read_total);
	write(1, "Copy successfully\n", 19);
	close(f1);
	close(f2);
	
	return 0;
}

这段代码用的是操作系统底层的read和write的函数,实际读取的文件我随便选择了一个pdf文件,以保证各种字符组合都能被测试到。但在实际读取的时候,往往总是在读到200~600字节的时候就终止读取了。经过一个字符一个字符的比对,我发现总是在读取到0x1A的时候:
原文件
上面的是原始文件
读取的文件
这个是读取到的文件

可以看到,确实是在遇到了1a这个字符的时候读取停止了。查了资料之后发现,这是因为原始的原先DOS系统会把0x1A当作EOF文件结尾符,之后的微软也继承了dos的内核,所以也会存在这个问题。虽然有文章表明后续的windows系统已经解决了这个问题,但是就我做实验得到的结果来看依然会有这种问题(我使用的是安装在Windows10系统上的mingw编译器)。本来想使用VS试一下会不会用有同样的问题,但是微软的vs对这种原始函数支持性太差了,程序放上去后各种deprcated的错误,遂放弃了在vs上实验的想法,直接跳到linux上实验。最终发现这种情况在linux上并不存在,我试了一个300MB的PDF,几乎瞬间就复制完了,而且也可以正常打开。由此可见Linux系统并不会将某个ascii码当作EOF文件结尾符来对待。在网上查资料后发现这篇博文对该机制有完整的解释:

https://cloud.tencent.com/developer/article/1527939?from=information.detail.linux%E5%88%A4%E6%96%AD%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E7%BB%93%E6%9D%9F%E7%AC%A6

总结该篇博文就是,文件中是不会存储文件结尾符的,实际的EOF是当文件意外终止或者文件结束的时候代码抛出的标识符。因此正常来说是不应当在读取文件的时候读到这个值的。有理由怀疑windows的相应机制存在一些问题。
此外在《C Programming language》一书中有如下对EOF的代码解释:

int _fillbuf(FILE *fp)
{
	int bufsize;
	
	if ((fp->flag & (_READ | _EOF | _ERR) != _READ)
		return EOF;
	bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZE;
	if (fp->base == NULL)
		if ((fp->base = (char *)malloc(bufsize)) == NULL)
			return EOF;
		
	fp->ptr = fp->base;
	fp->cnt = read(fp->fd, fp->ptr, bufsize);
	if (--fp->cnt < 0) {
		if (fp->cnt == -1)
			fp->flag |= _EOF;
		else
			fp->flag |= _ERR;
		fp->cnt = 0;
		return EOF;
	}
	return (unsigned char) *fp->ptr++;
}

尝试寻找解决办法
在该篇博客中作者提出了两个解决办法:

https://blog.csdn.net/apollon_krj/article/details/80020936

文中作者提出了两种解决办法,其中一种是通过fopen函数来完成该功能,另一种是通过每次遇到0x1A后判断是否到达文件末尾。虽然最终没有解决我想要达成的目的,但文章中提到的fopen函数可以正常读取文件,仍对我未来继续研究该问题给出了一些希望,最后给出一个fopen函数实现方法:

https://blog.csdn.net/iu_81/article/details/2349805

文章中最后给出了一个类似fopen的函数的实现,虽然是微软定义实现,但是仍可以发现是有特殊的符号用于标识是否以二进制读取文件。说明确实有相应的变量定义允许我们可以以二进制的方法文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值