关键问题:
1.使用流的形式,读取原文件,写入到分割的子文件中
2.多个子文件使用从0开始的纯数字作为文件名,并在原文件所在路径下新建文件夹,保存子文件
3.子文件合并时,需要原文件的文件名与后缀,所以分割时,使用txt文件保存原文件的文件名与后缀
4.合并时,选择分割生成的文件夹下的任意文件,通过遍历读取该文件夹下的子文件还原文件
效果截图:
关键代码:
这里只放分割文件的处理代码与合并文件的处理代码:(部分变量需要联系到界面组件,不过也很好理解,可自行体会)
分割文件:
//点击“打开文件按钮”的响应函数
void CFileToolDlg::OnBnClickedButton1()
{
TCHAR tszParam[200];
CString strFileName;
// 设置过滤器
::_tcscpy_s(tszParam, sizeof(tszParam)/sizeof(TCHAR), _T("文件"));
::_tcscat_s(tszParam, sizeof(tszParam)/sizeof(TCHAR), _T("|*.*||"));
// 构造保存文件对话框
CFileDialog FileOpenDlg(TRUE, NULL, NULL, 0, tszParam);
//文件对话框选择取消
if (FileOpenDlg.DoModal() == IDCANCEL)
{
return;
}
//获得对话框中输入文件的完整路径,同时包括文件名及扩展名(*.*)
m_strPathName = FileOpenDlg.GetPathName();
//获得文件名(*.*)
m_strFileName = FileOpenDlg.GetFileName();
//获得扩展名
m_strFileExt = FileOpenDlg.GetFileExt();
//获得文件名
m_strFileTitle = FileOpenDlg.GetFileTitle();
// 编辑框显示路径
GetDlgItem(IDC_EDIT1)->SetWindowText(FileOpenDlg.GetPathName());
}
//点击OK的响应函数
void CFileToolDlg::OnBnClickedOk()
{
// 将各控件中的数据保存到相应的变量 ,自动调用DoDataExchange
UpdateData(TRUE);
//创建线程处理文件
OE_CreateThread(OE_NULL, &FileThread, (OE_VOID *)this);
}
//文件处理线程函数
OE_VOID CFileToolDlg::FileThread(OE_VOID *lpParam)
{
CFileToolDlg *pObj = (CFileToolDlg *)lpParam;
pObj->FileFunc();
}
//获取文件长度
LL GetFileLength( string& filepath)
{
LL len = -1;
ifstream in;
in.open(filepath, ios_base::in);
if (in.is_open())
{
in.seekg(0, ios::end);//对输入文件定位,它有两个参数:第一个参数是偏移量,第二个参数是基地址
len = in.tellg();//它返回当前定位指针的位置,也代表着输入流的大小
in.close();
}
return len;
}
//创建文件目录,处理文件目录已存在的异常情况
void mkdir(string& path)
{
int i = 1;
while (access(path.c_str(), 0)!=-1)//文件目录存在,改名
{
path = path.substr(0, path.find_last_of('(')) + "(" + to_string(i) + ")";
i++;
}
string s = "mkdir " + path;
system(s.c_str());
}
//处理文件的函数
OE_VOID CFileToolDlg::FileFunc()
{
string message;//状态信息
if (m_strPathName.GetLength()==0)
{
//设置状态消息
SetDlgItemText(IDC_STATIC,"还未选择任何文件,请重试!");
return;
}
//获取用户选择的分割文件的大小
CString cstrChoosed;//选择分割文件的大小
m_combox.GetLBText(m_combox.GetCurSel(), cstrChoosed);
double fSize;//大小选择的float类型
string strChoosed = CT2A(cstrChoosed.GetBuffer());
stringstream ss(strChoosed);
ss >> fSize;
LL llBlockSize = (long long)(fSize*1024*1024*1024);//GB->MB->KB->B
//创建文件夹
string strFilepath = CT2A(m_strPathName.GetBuffer());
string strDir = strFilepath.substr(0, strFilepath.find_last_of('\\'))+"\\split";//拿到当前文件所在的路径,并创建子文件夹
string strDirTitle = CT2A(m_strFileTitle.GetBuffer());
strDir = strDir + "_" + strDirTitle;
mkdir(strDir);
//保存原文件名与后缀
FILE* pStream;
string strFileNamePath = strDir + "\\filename.txt";
if ((pStream = fopen(strFileNamePath.c_str(), "w")) == NULL)
{
AfxMessageBox(_T("打开文件失败!"));
return;
}
if (fputs(CT2A(m_strFileName.GetBuffer()), pStream)!=0)//写入文件发生错误
{
SetDlgItemText(IDC_STATIC, _T("保存文件名失败,请重试!"));
fclose(pStream);
return;
}
fclose(pStream);
//获取原文件大小
string strOriFilepath = CT2A(m_strPathName.GetBuffer());
LL llOriSize = GetFileLength(strOriFilepath);
if (llOriSize == -1)
{
SetDlgItemText(IDC_STATIC, _T("获取源文件大小失败!"));
return;
}
//二进制方式读取原文件
ifstream in;
in.open(strOriFilepath, ios_base::in | ios_base::binary);
if (!in.is_open())
{
SetDlgItemText(IDC_STATIC, _T("ifstream open failed"));
return;
}
//分割文件
char *buff = (char*)malloc(1024*1024*10);//读取缓冲大小,10MB
memset(buff, 0, 1024 * 1024 * 10);
LL llReadCount = 0;//已读取大小
LL llCurCount = 0;//一次读取的文件大小
for (int i = 0; llReadCount < llOriSize; ++i)
{
//设置状态消息
message = "开始分割文件(" + to_string(llReadCount) + "/" + to_string(llOriSize) + ")......";
SetDlgItemText(IDC_STATIC, message.c_str());
//二进制写入子文件
LL thisTimesRead = 0;//子文件已读取读取大小
string strDivFile = strDir + "\\" + to_string(i);//分割后子文件的文件名
ofstream out;
out.open(strDivFile, ios_base::out | ios_base::binary);//打开(新建)文件,二进制方式写入
if (!out.is_open())
{
SetDlgItemText(IDC_STATIC, _T("打开子文件失败!"));
free(buff);
break;
}
while (thisTimesRead < llBlockSize && llReadCount < llOriSize)//读取完一个块,或者文件已读取完,停止循环
{
memset(buff, 0, 1024 * 1024 * 10);
in.read(buff, 1024 * 1024 * 10);
llCurCount = in.gcount();//本次读取的文件大小
if (llCurCount <= 0)//读取失败
{
SetDlgItemText(IDC_STATIC, _T("读取原文件失败,请重试!"));
in.close();
out.close();
free(buff);
return;
}
llReadCount += llCurCount;//增加已读取文件
thisTimesRead += llCurCount;//增加子文件读取大小
if(!out.write(buff, llCurCount))
{
SetDlgItemText(IDC_STATIC, _T("写入子文件失败,请重试!"));
in.close();
out.close();
free(buff);
return;
}
//设置状态消息
message = "开始分割文件(" + to_string(llReadCount) + "/" + to_string(llOriSize) + ")......";
SetDlgItemText(IDC_STATIC, message.c_str());
}
out.close();
//UpdateData(false);
if (llReadCount == llOriSize)
{
//设置状态消息
message = "已完成";
SetDlgItemText(IDC_STATIC, message.c_str());
}
}
in.close();
free(buff);
}
//合并按钮,弹出新窗口,在新窗口中处理合并
void CFileToolDlg::OnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
//m_pMergeDlg.Create(IDD_DIALOG1, this);
MergeDlg m_pMergeDlg;
m_pMergeDlg.DoModal();
}
合并文件:
//文件处理线程函数
OE_VOID MergeDlg::FileThread(OE_VOID* lpParam)
{
MergeDlg* pObj = (MergeDlg*)lpParam;
pObj->FileFunc();
}
//判断path是否是一个文件夹
bool IsDir(const char* path)
{
WIN32_FIND_DATAA fd;
FindFirstFileA(path, &fd);//搜索一个文件或目录的名称相匹配的特定名称的目录
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)//文档值为FILE_ATTRIBUTE_ARCHIVE(32),文件夹值为FILE_ATTRIBUTE_DIRECTORY(16)
return true;
else
return false;
}
//获取文件长度
LL GetFileLength(const string& filepath)
{
LL len = -1;
ifstream in;
in.open(filepath, ios_base::in);
if (in.is_open())
{
in.seekg(0, ios::end);
len = in.tellg();
in.close();
}
return len;
}
//检查文件是否存在
void checkFile(string& path)
{
int i = 1;
while (access(path.c_str(), 0) != -1)//存在
{
path = path.substr(0, path.find_last_of('(')) + "(out" + to_string(i) + ")"+ path.substr(path.find_last_of('.'),path.length());
i++;
}
}
//处理文件合并的函数
OE_VOID MergeDlg::FileFunc()
{
string strMessage;//状态信息
//判断文件是否有效
if (m_strPathName.GetLength() == 0)
{
//设置状态消息
SetDlgItemText(IDC_STATIC1, "还未选择任何文件,请重试!");
return;
}
string strDir = CT2A(m_strPathName.GetBuffer());
string strDivDir = strDir.substr(0, strDir.find_last_of('\\'));
if (!IsDir(strDivDir.c_str()))//判断文件是否是文件夹
{
SetDlgItemText(IDC_STATIC, _T("error path!"));
return;
}
//取出原文件名与后缀,生成合并文件的文件名
FILE* pStream;
string fileNamePath = strDivDir + "\\filename.txt";
char line[200];
if ((pStream = fopen(fileNamePath.c_str(), "r")) != NULL)
{
if (fscanf_s(pStream, "%s", line, 200) == -1)
{
SetDlgItemText(IDC_STATIC1, "读取原文件名失败,请重试!");
fclose(pStream);
return;
}
fclose(pStream);
}
else
{
AfxMessageBox(_T("打开文件失败,可能是您选择的文件不是子文件,请重试!"));
return;
}
string strFileName(line);
string strOutFile = strDivDir.substr(0, strDivDir.find_last_of('\\'))
+"\\"
+ strFileName.substr(0, strFileName.find_last_of('.'))
+"(out)"
+ strFileName.substr(strFileName.find_last_of('.'), strFileName.length());//合并后的输出文件名
checkFile(strOutFile);//判断文件名是否存在,存在则改名
//二进制方式写入新文件
ofstream out;
out.open(strOutFile, ios_base::binary | ios_base::out);
if (!out.is_open())
{
SetDlgItemText(IDC_STATIC, _T("can not create merge.out!"));
return;
}
strDivDir += "\\";
char* buffer = (char*)malloc(1024 * 1024 * 10);//读取缓冲
LL llReadSize = 0;
int i = 0;//循环次数即文件名
while (1)
{
//设置状态消息
strMessage = "正在读取文件("+to_string(i)+")......";
SetDlgItemText(IDC_STATIC1, strMessage.c_str());
//二进制方式读取子文件
LL llCurlen = GetFileLength(strDivDir + to_string(i));//获取当前i子文件大小
LL thisTimesRead = 0;//当前子文件已读取大小
ifstream in;
in.open(strDivDir + to_string(i), ios_base::binary | ios_base::in);
if (!in.is_open())//文件不存在时退出循环
break;
while (thisTimesRead < llCurlen)//直到子文件读取完
{
memset(buffer, 0, 1024 * 1024 * 10);
in.read(buffer, 1024 * 1024 * 10);
int cur_count = in.gcount();//本次读取大小
if (cur_count <= 0)//读取失败
{
SetDlgItemText(IDC_STATIC1, _T("读取子文件失败,请重试!"));
in.close();
out.close();
free(buffer);
return;
}
thisTimesRead += cur_count;//子文件已读取大小
llReadSize += cur_count;//输出文件大小
if (!out.write(buffer, cur_count))
{
SetDlgItemText(IDC_STATIC1, _T("写入新文件失败,请重试!"));
in.close();
out.close();
free(buffer);
return;
}
//设置状态消息
strMessage = "合并中(已读取" + to_string(llReadSize) + "B)......";
SetDlgItemText(IDC_STATIC1, strMessage.c_str());
}
in.close();
++i;
}
out.close();
//设置状态消息
strMessage = "已完成";
SetDlgItemText(IDC_STATIC1, strMessage.c_str());
free(buffer);
}
//选择合并子文件按钮
void MergeDlg::OnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
TCHAR tszParam[200];
CString strFileName;
// 设置过滤器
::_tcscpy_s(tszParam, sizeof(tszParam) / sizeof(TCHAR), _T("文件"));
::_tcscat_s(tszParam, sizeof(tszParam) / sizeof(TCHAR), _T("|*.*||"));
// 构造保存文件对话框
CFileDialog FileOpenDlg(TRUE, NULL, NULL, 0, tszParam);
//文件对话框选择取消
if (FileOpenDlg.DoModal() == IDCANCEL)
{
return;
}
//获得对话框中输入文件的完整路径,同时包括文件名及扩展名(*.*)
m_strPathName = FileOpenDlg.GetPathName();
//获得文件名(*.*)
m_strFileName = FileOpenDlg.GetFileName();
//获得扩展名
m_strFileExt = FileOpenDlg.GetFileExt();
//获得文件名
m_strFileTitle = FileOpenDlg.GetFileTitle();
// 编辑框显示路径
GetDlgItem(IDC_EDIT1)->SetWindowText(FileOpenDlg.GetPathName());
}
void MergeDlg::OnClose()
{
//CDialogEx::OnClose();
DestroyWindow();
}
void MergeDlg::OnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
//创建线程处理文件
OE_CreateThread(OE_NULL, &FileThread, (OE_VOID*)this);
}
注释比较详细,完整代码就不放了。