-------------------------------------------
/*梦幻文件分割器 V1.0-----By alxen(小雨)
* 2007-04-07
*功能:分割一个大文件为几个较小的文件。软件运行在命令行下,分割文件时可以指定新文件的扩展名。
*软件的最小分割单位为1KB,没有考虑源文件小于1KB的情况。不是我偷懒,感觉做出来也不使用。
*因为没有几个人想要分割小于1KB的文件吧?(^_^)
*本软件的运行速度极快,分割一个几百M的文件不到一分钟搞定。
*使用方法:命令行下使用。命令格式如下:
*st <源文件> <分割大小(KB)> <.指定分割文件的扩展名(可选)>
*其中分割大小为整数,单位为KB。另外软件可以指定分割后的文件的扩展名,当然这只是强制改扩展名,不是格式转换,以满足具体的使用要求。
*软件原理:根据输入的分割大小,依次从源文件copy数据到一个新文件中,以实现分割。
当然分割前要有纠错处理,比如源文件不存在、分割大小大于源文件大小、参数不够等等。
*/
#include<iostream>
#include<iomanip>
#include<conio.h>
#include<cstdio>
#include<windows.h>
#include<io.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<share.h>
#include<cstdlib>
using namespace std;
void head(); //显示logo
void help(void); //输出软件使用说明
int argument_check(int argc,char *argv[]); //检测命令行参数,并检测是否指定了扩展名
void file_check(char *argv); //文件名检测
unsigned long size_detect(char *argv); //获得源文件的大小
unsigned long size_detect(unsigned long file_size,char *argv1,char *argv); //转换并判断输入的分割大小是否正确
int getfilename(char *argv,char *file_serial,char *expand_name); //获取源文件名函数
void cut(int file_name,char *file_serial,char *expand_name,char *s,FILE *fp1,unsigned long size); //主分割函数
void file_cut(char *argv,unsigned long file_size,unsigned long cut_size); //分割函数
void head() //显示软件logo
{
system("cls");
system("title 梦幻文件分割器 V1.0-----By alxen");
system("color 0b");
char *p[]={
"/t/t☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆/n",
"/t/t☆☆☆ 梦幻文件分割器--ST(Super cuT) V1.0 ☆☆☆/n",
"/t/t☆☆☆ By 小雨(alxen) 2007.04.06 ☆☆☆/n",
"/t/t☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆/n"
};
for(int i=0;i!=4;++i)
{
cout<<p[i];
Sleep(100);
}
cout<<endl;
}
void help(void) //输出软件使用说明
{
cout<<"/n/t/t/t梦幻文件分割器--ST(Super cuT) V1.0/n/
/n请在命令行中按以下格式输入/
/nst <源文件> <分割大小(KB)> <.指定分割文件的扩展名(可选)>/
/n例如/nst data.db 1024 或 st data.db 1024 .txt/
/n若文件不在当前目录下,则文件路径两边应加上/"/"/
/n例如/nst /"D://Programe Files//test//test.bak/" 2048"<<endl;
}
int argument_check(int argc,char *argv[]) //检测命令行参数,并检测是否指定了扩展名
{
//argv[1]是源文件名,argv[2]是分割大小,argv[3]是指定的扩展名
if(argc>4||argc<2)
{
if(argc>4)
cerr<<"/n参数过多!"<<endl;
help();
/*if(argc==1) //只输入了软件名st,很可能是直接双击打开。则输出提示信息后按任意键进入cmd转到当前目录下
{
cout<<"按任意键回到命令行下..."<<endl;
_getch();
system("cmd /c start /i"); //start /i用于在新cmd窗口中打开当前目录
exit(1); //一定要加cmd /c,执行后中断当前窗口。不然当前窗口不会关闭。st程序也不会退出。
}*/
_getch();
exit(1);
}
if(argc==4)
if(argv[3][0]!='.') //如果指定了扩展名则检查是否正确
{
cerr<<"你指定的扩展名/""<<argv[3]<<"/"有误。请重新指定。"<<endl;
exit(1);
}
else
return 1;//扩展名正确
return 0;
}
void file_check(char *argv) //文件路径及文件名检测
{
int n=_access(argv,0);
if(n!=0) //检测要分割的文件是否存在
{
cerr<<"文件 "<<argv<<" 不存在!/n请检查文件路径。"<<endl;
_getch();
exit(1);
}
}
unsigned long size_detect(char *argv) //获得源文件的大小
{
int fh;
long file_length;
_sopen_s( &fh, argv, _O_RDWR | _O_CREAT,_SH_DENYRD,_S_IREAD); //打开指定的文件
file_length=_filelength(fh); //获得源文件大小,单位byte
_close(fh);
return file_length;
}
unsigned long size_detect(unsigned long file_size,char *argv[]) //转换并判断输入的分割大小是否正确
{
double size=(double)file_size; //把源文件大小转换为double用于精确显示源文件大小
for(int i=0;argv[2][i];i++)
if(!isdigit(argv[2][i])) //如果输入了字母则提示出错
{
cerr<<"指定的文件分割大小/""<<argv[2]<<"/"不正确!/n源文件 "<<argv[1]<<" 大小为 "
<<fixed<<setprecision(2)<<size/1024.0<<" KB。请重新输入分割大小。"<<endl;
exit(1);
}
unsigned long length=atoi(argv[2]); //把输入的分割大小(字符数组)转换为数字赋给length(单位KB)
length*=1024; //把分割单位转换为byte
if(length>file_size||length<1) //如果输入的分割大小大于源文件的大小则提示出错
{
cerr<<"指定的文件分割大小/""<<argv[2]<<"/"不正确!/n源文件 "<<argv[1]<<" 大小为 "
<<fixed<<setprecision(2)<<size/1024.0<<" KB。请重新输入分割大小。"<<endl;
exit(1);
}
return length; //如果输入正确则返回转换后的分割大小
}
int getfilename(char *argv[],char *file_serial,char *expand_name,int expandname_check) //获取文件名函数
{
int i=0; //引用数组下标
for(int j=0;argv[1][j];++j)
if(argv[1][j]=='//') /*判断输入的文件名是否包含路径,若包含路径,则把j定位到最后一个/上,以获得
该/后面的文件名*/
i=j+1; /*如果输入的只是文件名,则i还是0,可以直接从数组argv[1]获取文件名*/
int d=0; //保存最后一个小数点的位置
for(int j=0;argv[1][j];++j)
if(argv[1][j]=='.') //如果文件名本身包含有小数点'.',则要定位最后一个'.',扩展名要取最后一个小数点后面的字符
d=j;
int t=0;
for(;i!=d;++i,++t)
file_serial[t]=argv[1][i]; //获得文件名(不包括扩展名,因为分割后的文件名后面要加序号)
file_serial[t]='-'; //在文件名和序号之间加'-'
file_serial[t+1]='/0'; //数组尾部置空 if(expandname_check==1)
for(int j=0;argv[3][j]!='/0';++j)
expand_name[j]=argv[3][j]; //如果指定了扩展名则把扩展名放在expand_name数组中
else
for(int j=0;argv[1][i]!='/0';++j,++i)
expand_name[j]=argv[1][i]; //源文件的扩展名存放在expand_name数组中
return t+1; //返回文件名数组中字符'-'的下一个位置
}
void cut(int file_name,char *file_serial,char *expand_name,char *s,FILE *fp1,unsigned long cut_size,unsigned long size_s) //主分割函数
{
char serial[30]; //仅存放文件序号(如1、2、3...)
_itoa_s(file_name,serial,30,10); //把file_name转换成字符串(就是文件序号)存放在serial字符数组中
strcat_s(file_serial,200,serial); //把file_serial中的文件名和文件序号链接起来组成分割后的文件全名
strcat_s(file_serial,200,expand_name); //把file_serial中的文件名和扩展名链接起来组成分割后的文件全名
FILE *fp2; //fp2是打开分割的文件的指针
fopen_s(&fp2,file_serial,"wb"); //以写方式打开最后一个分割的文件
cout<<"正在分割 "<<file_serial<<" ...";
if(cut_size>=size_s) //如果每个分割的文件大于数组大小,则要分多次从源文件取数据
{
unsigned long temp=cut_size/(size_s-1); //注意cut_size/(size_s-1)不一定整除,s数组最多能装size_s-1多数据
for(int i=1;i<=int(temp);i++) //这里同样要考虑最后一次从源文件取的数据块大小
{
fread(s,1,size_s-1,fp1); /*在第1次到第temp次从源文件取数据时都可以把数组s装满,即取够装满s数组
大小的数据块,亦即 size_s-1 bytes */
s[size_s-1]='/0'; //数组最后一定加结束符'/0'
fwrite(s,1,size_s-1,fp2);
s[0]='/0'; //复制完一块数据后把数组s清空,继续复制
}
if(cut_size%(size_s-1)!=0) /*特别注意,如果cut_size/(size_s-1)不能整除,则最后一次从源文件取的数据块
要重新计算。计算公式为 每个分割的文件大小-已经从源文件copy的数据*/
{
fread(s,1,(cut_size%(size_s-1)),fp1);
s[cut_size%(size_s-1)]='/0';
fwrite(s,1,(cut_size%(size_s-1)),fp2);
s[0]='/0'; //清空数组s
}
}
else //这种情况就是每个分割的文件大小 < 999999 bytes(即小于976KB),这个一次就可以用数组中转完
{
fread(s,1,cut_size,fp1);
s[cut_size]='/0'; //数组尾部置空
fwrite(s,1,cut_size,fp2);
s[0]='/0';
}
fclose(fp2);
}
void file_cut(char *argv[],unsigned long file_size,unsigned long cut_size,int expandname_check) //分割函数
{
FILE *fp1;
if((fopen_s(&fp1,argv[1],"rb"))!=NULL) //分割前再次判断要分割的源文件是否能打开
{
cerr<<"文件 "<<argv[1]<<" 打开错误/n"/
<<"请检查文件路径"<<endl;
exit(1);
}
else
{
int file_name=1; //分割后的文件名序号。从1开始
char file_serial[200]; //存放完整的文件名(文件名加序号)
char expand_name[10]; //存放源文件的扩展名
static char s[999999]; //源文件中转的数组,可存放976KB数据。该数组只能定义这么大了
unsigned long size_s=sizeof(s); //size_s是s数组的大小,即999999
int t=getfilename(argv,file_serial,expand_name,expandname_check); //获得文件名数组中字符'-'的下一个位置
int file_num=file_size%cut_size ? file_size/cut_size+1 : file_size/cut_size;
for(int i=1;i<=(int)(file_size/cut_size);++i) /*file_size/cut_size是要分割的文件个数,因为不一定整除,(即最后
一个分割的文件大小可能要小于cut_size),所以后面要考虑最后一个分割的文件大小*/
{
cut(file_name,file_serial,expand_name,s,fp1,cut_size,size_s); //调用主分割函数
cout<<"/tOK! /t完成 "<<file_name<<" 个,剩余 "<<file_num-file_name<<" 个。"<<endl;//接着cut函数的提示
file_name++; //文件名序号+1
file_serial[t]='/0'; //存放文件名序号的数组置空,继续分割下一个文件
}
unsigned long last_size=file_size%cut_size; //最后一个文件的大小(如果没有分割完的话)
if(last_size!=0) /*如果file_size/cut_size不是整除,则最后一个分割的文件大小就不是cut_size了,而是
file_size%cut_size bytes,即last_size,此时同样要 last_size 和999999的大小问题*/
{
cut(file_name,file_serial,expand_name,s,fp1,last_size,size_s); //调用主分割函数
cout<<"/tOK! /t完成 "<<file_name<<" 个,剩余 "<<file_num-file_name<<" 个。"<<endl; //至此,文件全部分割成功!
file_serial[0]='/0'; //存放文件名序号的数组置空
}
fclose(fp1); //关闭源文件
}
}
int main(int argc,char *argv[])
{
head(); //显示logo
int expandname_check=argument_check(argc,argv); //命令行参数个数检测,expandname_check检测是否指定了扩充名
file_check(argv[1]); //文件路径及文件名检测
unsigned long file_size=size_detect(argv[1]); //file_size是源文件的大小,单位bytes
double size=(double)file_size; //把源文件大小转换为double用于精确显示源文件大小
if(argc==2) //如果没有指定分割的文件大小则输出说明
{
cerr<<"/n/n/n你要分割的源文件 "<<argv[1]<<" 的大小为 "
<<fixed<<setprecision(2)<<size/1024.0<<" KB。/n"
"请指定分割的文件大小。/n格式为 st <源文件> <分割大小(KB)> <指定分割文件的扩展名(可选)>/n例如"
"/nst "<<argv[1]<<" 1024"<<endl;
return 1;
}
else
{
unsigned long cut_size=size_detect(file_size,argv); //判断输入的分割大小是否正确,cut_size是要分割的大小
cout<<"/n/n/n源文件 "<<argv[1]<<" 大小为 "<<fixed<<setprecision(2)<<size/1024.0<<" KB。/n"
"你选择的分割大小为 "<<cut_size/1024<<" KB。"
<<(expandname_check==1 ? "/n你指定的新文件扩展名为 " : " ") //如果指定了扩展名则显示提示的信息
<<(expandname_check==1 ? argv[3] : " ")
<<"/n源文件将被分割为 "
<<(file_size%cut_size ? file_size/cut_size+1 : file_size/cut_size)<<" 个子文件。"<<endl;
cout<<"确认分割么?按 N 键退出,按其它键继续。"<<endl;
char y=_getch();
if(y=='N'|| y=='n')
exit(1);
cout<<endl; //刷新缓冲区,因为上面输入了y的值。所以要刷新缓冲区。
file_cut(argv,file_size,cut_size,expandname_check); //调用分割函数分割文件。
}
cout<<"文件 "<<argv[1]<<" 分割完毕。"<<endl;
return 0;
}