利用c++builder设计文件分割机
利用c++builder设计文件分割机 利用C++ Builder 5.0实现文件切割机
作者:五大连池马明臣
一、问题的提出:
相信很多朋友都遇到过这样的问题,如何将一个大于一张软盘容量的文件拷贝到软盘上,可能读者会想到dos下的Backup与Restore之类的工具,但是这两个工具要求dos版本必须一致,否则备份后不能恢复,且使用起来非常不便,有没有方便的工具呢?答案是肯定的即文件切割机。目前网上有许多这样的工具,它可以轻松分割任意类型、任意大小的文件。并能自动提供合并批处理文件。如果你手头没有这类软件,又想分割软件,那么就跟我一起来学习如何利用C++ Builder设计文件切割机吧。
二、程序设计原理
我们知道文件之所以能够被分割,是因为所有文件都是以二进制的形式存储的,然后再根据文件系统的相应规定来进行读取处理。我们基于这个原理,把所有类型的文件都以二进制进行读写,合并时再以二进制的格式把分割后的文件重新组合起来,这样就实现了对文件的分割与合并。(注:也可以用dos的内部命令进行合并。格式如下: copy filename1/b+filename2/b+......filenamen/b filename 这里filename1--filenamen是被分割后的文件;filename是合并后的文件名 ;/b是以二进制的形式对文件进行拷贝。)
三、程序设计过程及详解
首先我们在c++ builder 中新建一个工程,存盘并命名工程文件为fgj.bpr,命名单元文件为filefgj.h。然后重新打开工程,更改窗体Form的Caption属性值为"明臣软件切割机1.0版"、Font属性值为"宋体""9号"、position属性值为 poScreenCenter,目的是使窗体居中显示; 在该工程中增添一个FileListBox控件(在Win3.1标签中)、一个DiretoryListBox控件(在Win3.1标签中)、一个DriveComboBox控件(在Win 3.1标签中)并修改DirList属性为DirectoryListBox1、一个FilterComboBox(在Win 31.标签中)并修改FileList属性为FileListBox1,这样修改以后四个关于文件目录的控件之间已经建立了关联,所有控件会随着其中之一的改变而改变,我们完全不必考虑更多的细节问题,很是方便(即实现了winodws文件管理器中的功能)。为了过滤不同的类型的文件,我们还需要修改FilterComboBox的Filter属性如下表:
Filter Name Filter
执行文件(*.exe) *.exe
压缩文件(*.zip;*.rar;*.arj;*.cab) *.zip;*.rar;*.arj;*.cab
影碟文件(*.dat;*.mpg;*.mpeg) *.dat;*.mpg;*.mpeg;*.miv;*.mp2;*.mpa;*.mpe
图像文件(*.bmp;*.jpg;*.gif) *.bmp;*.ico;*.wmf;*.emf;*.jpg;*.gif
所有文件(ALL Files) *.*
再添加一个ComboBox组合框(用来选择或输入文件分割后的大小),设置ComboBox的Style属性值为 csDropDown ,为了提供用户选择分割后的文件大小还要设置ComboBox的Ltems属性值如下:(注:该值可任意设置,笔者在这里设置成了标准软盘容量)
1400
1200
720
360
接着添加三个EDIT组件,再添加一个ListBox1列表框、添加3个位图按钮组件BitBtn并分别设置其Caption属性值为"切割文件"、"关于程序"、"退出程序";添加一个进度条ProgressBar1并设置其Min属性值为1、Max属性值为100 、Position 属性值为1 ;再 添加三个文字标签Label并分别设置其Caption属性值为""您选择的源文件名为:"、"源文件被分割成:"、"请输入生成的第一个目标文件名:"。然后按图1设置好各个组件的布局。
下面我们开始设计程序代码:
首先打开filefgj.h 头文件,在其头部加入#include "fstream.h"(生成批处理文件时要用到该单元文件)然后在 private: // User declarations后面添加如下变量
int FileHandle; // 文件句柄变量
int FileLength; //源文件大小变量
int num; //源文件被分割后的文件数目
int iBytesRead; //临时变量
char *pszBuffer;//临时变量
int buf; // 文件缓冲区变量1
int buf1;//文件缓冲区变量2
接着双击FileListBox组件编写该组件的响应事件如下:
void __fastcall TForm1::FileListBox1Change(TObject *Sender)
{
Edit1->Text=FileListBox1->FileName;//在edit1中显示你选择的文件名
Edit3->Text=ChangeFileExt(FileListBox1->FileName,"."+IntToStr(1));//用于显示分割后的第一个文件名
buf=((ComboBox1->Text).ToInt());//设置文件缓冲区为组合框中选择的或输入的大小
buf1=buf*1024l;//把缓冲区转化为字节
FileHandle=FileOpen(Edit1->Text, fmOpenRead);//打开文件句柄
FileLength=FileSeek(FileHandle,0,2);//测试文件长度
num=FileLength/buf1+1;//计算分割的文件个数
Edit4->Text="源文件被分割成"+IntToStr(num)+"个文件";//在edit4中显示分割的文件个数
FileClose(FileHandle);//关闭文件
ListBox1->Items->Clear();//清空列表
ListBox1->Items->Add(ChangeFileExt(Edit3->Text,".bat").c_str());//在列表第一项中显示生成的合并批处理文件名
for (int i=1;i<=num;i++)
{
ListBox1->Items->Add(ChangeFileExt(Edit3->Text,"."+IntToStr(i)));//在列表中显示所有分割后的文件名路径
}
}
//-------------------------------------------------------------
然后双击ComboBox1组件编写如下响应代码:(并把OnChange、OnEnter的响应代码也设为该段代码)
void __fastcall TForm1::ComboBox1Click(TObject *Sender)
{
buf=(ComboBox1->Text.ToInt());//把组合框中的值赋给文件缓冲变量
buf1=buf*1024l;
num=FileLength/(buf*1024L)+1;//计算分割后的文件个数
Edit4->Text=num;//在edit4中显示文件个数
ListBox1->Items->Clear();
ListBox1->Items->Add(ChangeFileExt(Edit3->Text,".bat").c_str());
for (int i=0;i<num;i++)
{
ListBox1->Items->Add(ChangeFileExt(Edit3->Text,"."+IntToStr(i)));//在列表框中显示分割后的全部文件
}
}
//----------------------------------------------------------------------------------------------------------
双击切割文件按钮编写切割按钮的响应函数如下:
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
if (FileLength<=buf1)
{ShowMessage("文件小于要分割的大小,不用分割");
return;}
ProgressBar1->Max=num;//设置进度条状态
ProgressBar1->Position=1;
int i=1;
pszBuffer = new char [buf1];
int temp=buf1;
for (i=1;i<=num;i++)
{
if (i==num) pszBuffer = new char [FileLength-buf1*(num-1)];
if (i==num) temp=FileLength-buf1*(num-1);
FileHandle=FileOpen(Edit1->Text,fmOpenRead);//打开文件句柄
FileSeek(FileHandle,buf1*(i-1),0);//定位读文件位置
if (FileRead(FileHandle, pszBuffer,temp)!=temp) ShowMessage("Read error!");
FileClose(FileHandle);//关闭源文件
ProgressBar1->Position=i;
Label5->Caption="已进行:"+AnsiString(ProgressBar1->Position*100/num)+"%";
int Handle=FileCreate((ChangeFileExt(Edit3->Text,"."+IntToStr(i))));//生成分割后的目标文件
if (FileWrite(Handle,pszBuffer,temp)!=temp) ShowMessage("Write error!");//把软件写入分割后的目标文件
FileClose(Handle);//关闭目标文件
}
ProgressBar1->Position=1;
Label5->Caption="完成分割";
//------------------------------------ 以下是自动生成合并文件时的批处理文件代码
AnsiString name="/b";
for (int i=num;i>=1;i--)
{
AnsiString nn=ChangeFileExt(Edit3->Text,"."+IntToStr(i));
if (ExtractFileName(FileListBox1->FileName).Length()>12)
{nn=ExtractShortPathName(FileListBox1->FileName);
nn=ChangeFileExt(nn,"."+IntToStr(i));
}
if (i==1)
{name=ExtractFileName(nn)+name;}
else
{
name="/b+"+ExtractFileName(nn)+name;}
}
AnsiString hbbat="copy "+name+" "+ExtractFileName(FileListBox1->FileName);
fstream file;
file.open(ChangeFileExt(Edit3->Text,".bat").c_str());
file<<" @echo off"<<'\n';
file<<"echo Created by Mc-Splite Reading split files:"<<'\n';
file<<hbbat.c_str();
file.close();
}
//----------------------------------------------------------------------------
双击"关于程序"位图按钮编写"关于程序"的响应代码如下:
void __fastcall TForm1::BitBtn2Click(TObject *Sender)
{
ShellAbout(Handle,"","\n明臣文件切割机(V1.00) 版权所有(C)马明臣\n地址:黑龙江省五大连池市统计局 2002.6\n联系电话:6322348 Email: M@0451.com ",NULL); //利用api函数显示版板信息
}
双击"退出程序"位图按钮编写"退出程序"的响应代码如下:
void __fastcall TForm1::BitBtn3Click(TObject *Sender)
{
Close();
}
现在我们可以按F9键来运行一下,怎么样分割/合并几个文件试试,效果不逊色于网上的这类共享软件吧!还不亲自动手试一试。
如果你想单独发布这个程序,你可以打开Project|Option.对话框去掉Package|Build with runtime packages、Linker|Create Debug Infomation 和LInker|Use dynamic RTL前的选择再编译一次就可生成不依赖于任何动态链接库可独立运行的软件了。