当前位置:编程学习 > 网站相关 >>

自己打造文件切割机

文/图 灰狐(Bink && E.S.T)
===================================
对于我们这些玩黑的同学来说,U盘都是必备用品吧,不管是拷作业、工具甚至电影等几乎都离不开它,但别忘了,它的大小可是有限的哦。
假如寒暑假的你要回家,肯定不会想整天无所事事吧?带上咱的工具,回家继续弄。唉,咋一个编译器都要几百M啊,咱只有一个256M的U盘和一个MP3,总不能不带吧?不用怕,因为我有文件切割机!当然了,这些工具网上也有不少,但这年头网上还有多少地方值得放心呢?还是自己做一个比较安心,顺便复习一下编程。下面我们就亲手打造一个属于自己的文件切割机。
我的编译环境是Borland C++ Builder 6.0 + Windows XP SP2。
程序思路:以二进制方式读取文件,然后不断地拷贝到其他生成文件中,合并的时候再完整地读取到一个文件中就可以了。本程序有一个很明显的特点,就是不考虑文件格式。
对于界面的排布我就不多废话了,参考界面如图1所示。这里只拣几个重要的说明一下,Edit1为源文件选择,Edit2为目的目录选择,Edit3是要分割后的单个目标文件大小。
 
图1

下面我们以按体积分割为例开始讲解代码的编写(限于篇幅,以下代码有所删减,完整源代码请查看打包文件)。

FILE *fp,*temp;
fp=fopen(Edit1->Text.c_str(),"rb");
fseek(fp,0,2);
float filesize=ftell(fp);        //文件大小,单位:字节
fclose(fp);

char filename[MAX_PATH];         //保存文件名
//分割后每个文件的最大体积,单位MB
int  BufferSize=(StrToInt(Edit3->Text))*1024*1024;
int  Temp=BufferSize; //文件体积,同上
char* buffer=new char[BufferSize]; //文件读写缓冲区
int j=1;
int num=StrToInt(FileNumberLabel->Caption);
//要分割成的文件个数
//进度条做好准备
pBar->Visible=true;
pBar->Min=0;
pBar->Step=1;
pBar->Max=num;
pBar->StepIt();

for(j=1;j<=num;j++)
{
if(j==num) //如果是最后一个文件,体积可能小于BufferSize,特别处理
{
buffer=new char[filesize-BufferSize*(num-1)];
Temp=filesize-BufferSize*(num-1);
}
Application->ProcessMessages();
//使程序在循环中同样能接受外界消息
sprintf(filename,"%s%d_%s",Edit2->Text.c_str(),j,ExtractFileName(Edit1->Text));
temp=fopen(filename,"wb");
fp=fopen(Edit1->Text.c_str(),"rb");
fseek(fp,BufferSize*(j-1),0);
fread(buffer,1,Temp,fp);
fwrite(buffer,1,Temp,temp);
fclose(fp);
fclose(temp);
pBar->StepIt();         //进度条走动一步
}
MessageBox(NULL,"文件切割已完成!","提示",NULL);
return;

假设我们的文件是3.5M,目标文件大小是1M,很明显我们要分割成4个文件,前三个都是1M,最后一个是0.5M,这样我们开始时的变量就分别是这样分布的:BufferSize=Temp=1*1024*1024(字节),num=4,所以我们的循环范围是for(j=1;j<=num,j++),里面的ExtractFileName的作用是从全文件名中把纯文件名提取出来,并使用sprintf把目标目录和纯文件名结合起来。假设我们的文件是“D: est est.rar”,目标路径是“E:abc”,这样处理后,filename的内容就是“E:abc1_test.rar”,数字从1开始自加,在这里会一直加到4,然后使用temp指针指向它,fp指针指向源文件,“fseek(fp,BufferSize*(j-1),0);”从源文件中读取一定字节数据(这里1*1024*1024字节)后读入到temp指针中,之后关闭文件指针,进行下一次循环。在最后一次,即j=4的时候剩下的数据已经不足1M,所以在循环的开头会做出判断,使文件缓冲区的容量相应减小,这也是我们声明的时候使用“char* buffer=new char[BufferSize];”而不是“char buffer[BufferSize];”的原因。
合并的时候相对来说会简单一些,只要打开第一个文件,然后顺序把其他文件的数据读入到第一个文件的尾部就可以了,不过实现起来还是需要一番功夫的,实现代码如下。

AnsiString FileName=ExtractFileName(Edit1->Text);
filename1=FileName.c_str();
sprintf(filename2,"%s",filename1);
if(filename2[0]<=48 || filename2[0]>=58 )
{
MessageBox(NULL,"您的文件不合法,请重新选择!","警告",NULL);
return;
}

这一部分代码是把文件名提取出来后将其转换成数组类型,然后判断第一位是不是数字以确定是否为分割后的文件,这只是个简单的判断方法,但也总比不作任何检查就直接合并来的好了,呵呵。

sscanf(filename2,"%d_%s",&i,&tempname);

我们再通过上面这一句将文件名中的数字与原纯文件名分离出来。

bool flag=true;
while(flag)
{
sprintf(filename,"%d_%s",i,tempname);
fp=fopen(filename,"rb");
if(fp==NULL)
{
i=i-1;
flag=false;
break;
}
fclose(fp);
i=i+1;
}
ShowMessage("找到符合条件的文件共有"+AnsiString(i)+"个");

上面这段代码是用来搜索符合条件的文件共有多少个,最后找到的个数为i个。我们继续看最核心的代码。

sprintf(filename,"1_%s",tempname);
fp=fopen(filename,"ab");        //打开1_test.rar
for(int num=1;num<=i,j<=i;num++)
{//循环条件:循环次数等于总文件个数,j不能超过i
sprintf(filename2,"%d_%s",j,tempname);
//此时j为2,详见完整代码

temp=fopen(filename2,"rb");        
//从2_test.rar开始打开文件
fseek(temp,0L,2); //指针移动到末尾
int FileLen = ftell( temp ); //得到文件大小
fseek(temp, 0L, 0);         //指针重新移动到文件开头
char *FileBuf = (char *) malloc( FileLen );
//分配等于文件大小的缓冲区

fread( FileBuf, 1, FileLen, temp );
//将数据读出来后读入到文件1_test.rar中
fwrite( FileBuf, 1, FileLen, fp );
free( FileBuf );        //释放缓冲区
j=j+1;                        //文件名中的数字自加1

fclose(temp);
DeleteFile(filename2);
//该文件里的内容已被读入到源文件,没有用处了
}
fclose(fp);
ShowMessage("合并处理完毕! 合并后的文件名为"+String(filename));

以上就是一个基本完整的合并代码了,当然,你可以加上自己的代码以更加完善。现在我们F9运行一下,图2是我的测试结果。
 
图2

测试结果表明,分割的时候一个400M左右的文件大约需要1分钟,合并的速度稍微快一点,大概在50秒左右。需要注意的是,如果设置体积太大,比如为50M的话,进度条会更新地非常缓慢,这也算是个BUG吧,希望能用你的聪明才智可以完美解决。
最后说一下,本程序没有使用多线程技术,因为对于这种程序,从理论上讲,多线程的意义在这里并不会使你的程序分割速度变得更快,不到1分钟的时间也没有必要使用暂停,所以就没有采用多线程。呵呵,不能搞惟技术论嘛

补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,