c语言一个一个读取文件字节,[旧文整理]C语言文件读写练习_一个文件复制程序...

这几天浏览了很多人的博客,发现似乎有这么个规律: 大神的blog中代码很少甚至没有一行代码,且大多是写一些认真思考出来的东西, 而像我这样的新手blog好多都是"贴代码",有时候连两句"废话"都懒得扯。

莫非水平越高 代码 在博客内容中所占的比例就越少? 那就努力多写点思考的"成果"吧,也许这就是 大神之路 吧。

正文

这次整理的是我第一次用C写的文件复制程序。 那时候为了理解流(Stream)这个东西也算是费了心思了, 印象最深刻的便是把 流 比作 管道,数据通过管道流通。 到现在也不敢说完全理解,对新手而言,抽象的概念远不如具体的代码让人安心。

对这些抽象的概念,我的一点点体会就是, 一时不能理解没什么关系,多写代码体验一下,多思考,慢慢的也就悟出来了. 也许就像在 《.Net4.0面向对象编程漫谈》 一书中听金老师说的那样: 学习编程,大家都在"盲人摸象"。 我想,摸得多了,也就知道"大象"是什么样了。

这次旧的代码就不贴出来了,想吐槽去看看 旧文 吧。

旧文中是按文本流复制的,如果仅仅是复制文本文件还行, 但是如果想要复制非文本文件,可能会出现问题。 经测试,即使文件打开方式是"rb","wb"也不能正常复制exe文件,原因应该在那个while循环上了。 因此这次改成以二进制流的形式复制。 这样应该能用于所有类型的文件。 旧文中是一次复制一个字符,一般来说,这样的效率不高。就像上一篇 [旧文整理]打印所有真值组合的练习题,整理并改进 中那样,频繁地调用IO函数,性能损失特别大。所以这次同样尝试使用"缓存", 我的方案是使用fread() 和fwrite()函数按字节块复制。

重新写的程序代码还是要贴出来的,呵呵:

#include

/* * * * * * * * * * * * * * * * * * *

* CopyFile: 此函数用于复制指定的文件到指定的位置,

* 功能算是Windows下的 copy, 或linux下的 cp.

* 这里的实现用了 fread() 和 fwrite(), 二进制流的形式读写

* 返回值: 非负数: 复制的字节数.

* 小于0的数: 请检查文件名是否正确输入, 是否合法.

* * * * * * * * * * * * * * * * * * */

long CopyFile(const char* file_1, const char * file_2)

{

FILE *pfRead = fopen(file_1, "rb");// pfRead用作复制源.

FILE *pfWrite = fopen(file_2, "wb");// pfWrite对应复制出来的新文件.

if (NULL == pfRead || NULL == pfWrite)

{

fclose(pfRead);

fclose(pfWrite);

return -1;

}

long bytesCount = 0;//统计复制的字节数. long最大可以表示不超过2GB的文件

/* 因为C没有byte类型,所以这里用char替代

* 貌似对大多数机器来说,char都是 单字节

*/

int arrLen = 1024; //这个是缓存数组的元素大小

char bufArr[arrLen]; //这个是 "缓存", 缓存的字节数是 elementSize * arrLen.

int copiedLen; //这个变量用来记录fread函数每一次真正读取的元素数

int elementSize = sizeof(bufArr[0]);

do

{

copiedLen = 0;

copiedLen = fread(bufArr, elementSize, arrLen, pfRead);

fwrite(bufArr, elementSize, copiedLen, pfWrite);

bytesCount += copiedLen * elementSize;

} while(copiedLen == arrLen);

//关闭流.

fclose(pfRead);

fclose(pfWrite);

return bytesCount;

}

/* 此程序从命令行参数 获取文件名(路径):

* C:\>application_name file1_name file2_name

* Example:

* C:\>cfilecopy.exe e:\abc.txt f:\abc2.txt

* abc.txt必须存在,abc2.txt可以不存在,会自动创建文件.

* 如果abc2.txt已存在,会被覆盖掉.

* (友情提示:注意保存重要的文件, 别被"盖"了!)

* */

int main(int argc, char *argv[])

{

if(argc == 3)

{

char * f1 = argv[1];

char * f2 = argv[2];

printf("Copy File 1: %s\n To File 2: %s\n", f1, f2);

puts("Copying......");

long bytesCount = CopyFile(f1, f2);

if (bytesCount < 0)

{

puts("Fail to copy.");

}

else

{

printf(" %ld bytes wrote into %s.\n", bytesCount, f2);

}

}

else

{

puts("parameters error");

}

//getchar();

return 0;

}

这个是stdio.h头文件中的声明:

/*

* Direct Input and Output Functions

*/

_CRTIMP size_t __cdecl __MINGW_NOTHROWfread (void*, size_t, size_t, FILE*);

_CRTIMP size_t __cdecl __MINGW_NOTHROWfwrite (const void*, size_t, size_t, FILE*);

当我看到fread() 和fwrite()的声明,并且了解各个参数的含义之后, 我就有了一个问题: "缓存"数组的类型,int 和char或者其他类型会对函数的执行有什么影响? 有这个问题,是因为我不清楚fread() 和fwrite()的工作方式,如果他们是一次读写一个相应类型大小的字节块, 那么用不同的类型性能可能就会不一样。不过经过测试(测试时分别用了char 和int,调整arrLen, 保证数组的字节大小是一样的,即如果是 int bufArr[256], 那么相应的有 char bufArr[1024]做对比), 我并没有发现类型对性能有什么明显影响。 对性能的影响主要反映在缓存的字节大小上。

为了验证复制出来文件与源文件是否一样, 我找了个文件指纹校验软件比较生成的MD5值。 结果显示复制出的文件和源文件是完全一样的。 找了一个大约17.5MB的文本文件(用记事本打开 巨卡)。

使用旧版的程序复制文本文件, 测试三次:2.059s, 2.057s, 2.057s。

改进过的程序,当缓存设置为 1 字节时:2.126s, 2.118s, 2.113s。 这个相当于一次复制一个字节,还不如旧版的呢。

当缓存设置为 2 字节时:1.095s, 1.075s, 1.101s。 优势体现出来了

当缓存设置为 4 字节时:0.572s, 0.579s, 0.574s。 性能几乎是线性提高。

当缓存设置为 8 字节时:0.322s, 0.355s, 0.320s。 提升幅度开始下降了,常数时间的影响越来越明显。

直接把缓存设置为 512 字节:0.062s, 0.068s, 0.062s。 性能已经非常好了。

继续加大缓存,性能没有任何提高了,这个时候物理磁盘的速度应该是瓶颈了

以前只是在书上看到过IO对程序性能的影响的描述,但是真正自己实践一下, 还是被这种效果吓了一跳,这也算是给了我们一个提示: 对程序中IO相关的模块进行性能优化,往往能获得奇效。

Others

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值