MFC界面——实现分割与合并文件

该博客介绍了如何使用C++实现文件的分割与合并功能。程序通过读取原文件,将其分割成多个小文件并保存在指定文件夹下,每个子文件使用数字命名。在合并时,程序能根据保存的原文件名信息,将子文件还原成原始文件。整个过程涉及文件的读写操作,线程处理以及异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关键问题:

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);
}

注释比较详细,完整代码就不放了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值