第二篇 从病毒的自我复制谈文件的基本操作(一)

        还记得以前学习C语言时,做结业的课程设计,做的是学生学籍管理系统什么的。当时有一条要求是,最后将结果以文件形式保存。可我硬是没弄明白“文件”形式到底是什么。哈哈,是的,当时我就那么傻。谭浩强的书,还没翻到“文件”那个地方,一学期就结束了。

        的确,对于大多数教材,都将大量的精力放在基本语法的讲解上,而对我们实际非常关心的文件说得并不是那么详细。C语言的文件是什么呢?其实没有什么特别的,我们硬盘里的躺着的那么多txt、mp3、doc、avi的,不就是文件吗?~对,C语言里面说的文件和这个其实是一回事。

        为了让大家相信,你可以编写一个这样的小程序。它将在程序当前目录下建立一个"example.txt"的文本文件。

        #include <stdio.h>

        int main()
        {
             FILE *fp=fopen("example.txt","w"); //文件名别忘了后缀啊~~~
             if(!fp)return -1;                              //若打开失败的话……
             fprintf(fp,"这是一个文本文件!/n");
             fclose(fp);
             return 0;
         }

        在Dev-C++下编译然后运行(其他编译器应该也可以,不过我没有试过。以后各程序,如果没有特殊说明的话,我都是使用Dev-C++编译的)。你可以看见一个命令提示符的“黑框框”一闪而灭。这是程序运行过的标志。不过我们在这里并不需要这个“闪现”的黑框。所以我们可以在工具>>编译选项>>代码生成/优化>>连接器 里找“不产生控制台窗口”。将后面设置为“Yes”。好了,重新编译运行。我们可以发现,这次黑框不见了~

        回过头来,我们可以发现,在程序所在的目录下,多了一个叫做"example.txt”的文本文件。双击打开看看。“这是一个文本文件!”恩,对了。我想大家已经把C语言里的文件和硬盘里躺着的文件联系到一起了吧?

        好了,扫误区就到这里。现在要说的是病毒的自我复制功能的实现。其实这个问题,可以简化为二进制文件的复制问题。那么如何实现二进制文件的复制呢?如果大家学过谭浩强的“文件”的话,就知道w、r是以ASC方式读写字符,而wb、rb就是以二进制方式读写。这个就是我们需要的。至于思路,很简单:

        以rb方式打开欲复制的文件A,再以wb方式新建一个目的文件B。然后,从A中读取字符,写入B文件就好了。还有一点,C语言的文件的文件名其实是可以包含路径的,由于‘/’被作为转义字符,所以需要使用‘//’才能用来描述文件的路径。比如,一个C:/WINDOWS/system32的explorer.exe(资源管理器),我们需要用这样的字符串表示来表示它:const char *path="C://WINDOWS//system32//explorer.exe"。

        光说不练假把式。下面我们来看一个例子。看完大家就明白了。下面这个代码我们将名字命名为selfcopy.c,也就是说,编译后将产生一个名为selfcopy.exe的可执行程序。程序的执行,是将这个程序自身复制到C:/WINDOWS/文件夹下。

        #include <stdio.h>

        const char *path_name="C://WINDOWS//selfcopy.exe";
        const char *self="selfcopy.exe";

        int main()
        {
             char tmp;
             FILE *from,*to;
             if(!(from=fopen(self,"rb")))return -1;              //以二进制只读方式打开原文件
             if(!(to=fopen(path_name,"wb")))return -1;    //以二进制只写方式新建目的文件
             while(!feof(from))
             {
                 tmp=fgetc(from);                                      //从原文件读一个字符到内存
                  fputc(tmp,to);                                           //将内存中的字符写入到目的文件
            }
              fclose(from);
             fclose(to);
             return 0;
         }

        怎么样?够简单吧?你可能会说,我怎么知道复制过去的就是本身这个程序呢?你可以将C/WINDOWS/下的selfcopy.exe剪切下来,覆盖原来的编译产生的程序,看看结果是不是还是一样呢?我想你可以得到一个满意的答案。

        当然,这里的文件如果过大的话,这样一个字符一个字符的读写,其实是一个比较耗时的做法。原因是因为读取一次硬盘是一个非常复杂的过程,硬盘寻道也非常耗时。值得庆幸的是,硬盘寻道寻找到的是一个扇区,而一个扇区一般存储空间是512字节。这就意味着,如果我们需要节省时间的话,我们没必要一个字节一个字节的读写内容,而是以512字节为基本单位。那么有人也许会问,如果这个文件不是512字节的整数倍,那么最后一次读入的时候会发生什么情况呢?呵呵,细心的孩子~ 不过这个问题其实C语言已经很好的解决了。我们需要使用的是两个函数:fread()和fwrite()。fread将实际读入的字符数作为返回值返回,我们接收到这个返回值,就可以确定我们应该用fwrite实际写入多少个字符。于是,上面那个程序的改良版如下:

        #include <stdio.h>

        const char *path_name="C://WINDOWS//selfcopy.exe";
        const char *self="selfcopy.exe";

        int main()
        {
             char tmp[512];                    //作为缓冲区
             unsigned int count;             //用于计数实际读入的字符数
             FILE *from,*to;
             if(!(from=fopen(self,"rb")))return -1;
             if(!(to=fopen(path_name,"wb")))return -1;
             while(!feof(from))
             {
                  count=fread(tmp,1,512,from);    //计划读入一个扇区
                  fwrite(tmp,1,count,to);                //写入实际读入的字符数
             }
             fclose(from);
             fclose(to);
             return 0;
        }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值