FunctionGHW

立志成为真正程序员的码农,伪全栈,目前主要写C#

[旧文整理]C语言文件读写练习_一个文件复制程序

FunctionGHW posted @ 2013年3月24日 02:28 in C语言 with tags c IO开销 优化 fread fwrite , 8968 阅读

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

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

正文

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

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

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

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

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

#include <stdio.h>
/* * * * * * * * * * * * * * * * * * *
 * 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_NOTHROW	fread (void*, size_t, size_t, FILE*);
_CRTIMP size_t __cdecl __MINGW_NOTHROW	fwrite (const void*, size_t, size_t, FILE*);
                    

当我看到fread()fwrite()的声明,并且了解各个参数的含义之后, 我就有了一个问题: "缓存"数组的类型,intchar或者其他类型会对函数的执行有什么影响? 有这个问题,是因为我不清楚fread()fwrite()的工作方式,如果他们是一次读写一个相应类型大小的字节块, 那么用不同的类型性能可能就会不一样。不过经过测试(测试时分别用了charint,调整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

依旧留下旧文链接: 做几个有关文件读写的练习

 

Liwovosa 说:
2021年4月22日 15:31 Excellent blog! I found it while surfing around on Google. Content of this page is unique as well as well researched. Appreciate it. click here
aman 说:
2021年8月13日 21:43

Really great post. I enjoy reading this article. There is such a true article there is more information for us.
<a href="https://9anime.surf/">9anime app</a>

Կանադայի վիզայի դիմո 说:
2021年11月22日 15:34

The first phase the preparation should, theoretically, be uninfluenced by the intended intensity and duration of the sound which is subsequently produced. In fact, however, so quickly are the three phases accomplished that the pianist rarely has capacity to think, in performance, of each phase separately.

Zahtjev za vizu za K 说:
2021年11月23日 15:39

This article gives the light in which we can observe the reality. This is very nice one and gives indepth information. Thanks for this nice article.

indiai vízum online 说:
2021年11月24日 04:08

Great post I would like to thank you for the efforts you have made in writing this interesting and knowledgeable article.

Domanda di visto per 说:
2021年11月29日 22:59

Website especially this blog page. Among the lots of comments on your articles. Thanks for sharing

AP SSC computer Mode 说:
2022年9月11日 00:33

Computer Knowledge is very much essential nowadays. Even Checking the Results of various examinations, AP SSC computer Model Paper Downloading hall tickets and study materials for multiple exams and booking tickets etc ..need minimum Technical Knowledge is much more important for everyone. Since the Government of AP also provided Computer labs for students in all Government schools also. Technical Knowledge is much more important for everyone. Since the Government of AP also provided Computer labs for students in all Government schools also.

Emma 说:
2023年1月18日 19:03

Assuming you have a basic understanding of C, you can use the fread and fwrite functions to read and write data from and to files. In this example, we'll copy a file byte-by-byte. First, we'll open the types of CBD topical products source and destination files using the fopen function. The first parameter is the name of the file to open, and the second parameter is the mode in which to open the file. For reading, we use "r" mode, and for writing, we use "w" mode.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter