ifstream、ofstream进行文件的按行读写

本文档记录了使用C++标准库ifstream和ofstream进行文件操作,实现从FileA中移除FileB中相同内容并写入FileC的过程。主要涉及CFileDialog对话框选择文件,ifstream::getline()读取文件,以及CString成员函数处理文本内容。在解决FileC最后一行多出空行问题时,改用fopen_s()和fgets()函数来确保正确读取和处理文件的换行符。

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

对于使用ifstream、ofstream进行文件的操作平时用得比较少,过不了多久又忘了,记录一下,便于以后回顾及复用。

环境:Windows、Visual Stdio、MFC(Dialog base)

功能比较简单:从FileA 中移除FileB中相同的内容,再将不需要移除的信息写入FileC

FileA:
//------------Channel ------------
0xA0, 0xBB, 0xFF, 0x00, //R
0xA1, 0xCC, 0xFF, 0x01, //Gr
0xA2, 0xDD, 0xFF, 0x10, //Gb
0xA3, 0xEE, 0xFF, 0x11, //B

FileB:
//------------Channel ------------
0xA1, 0xCC, 0xFF, 0x01, //Gr
0xA3, 0xEE, 0xFF, 0x11, //B

FileC:
//------------Channel ------------
0xA0, 0xBB, 0xFF, 0x00, //R
0xA2, 0xDD, 0xFF, 0x10, //Gb

CFileDialog 创建选取文件对话框;
ifstream 创建文件流对象打开文件,**ifstream::getline()**按行读取文本再操作。
CString 成员函数 Remove()Left() 的使用等等。

.h
class C_Dialog
{
public:
	afx_msg void OnBnClickedButtonSelectSourceFile();
	afx_msg void OnBnClickedButtonSelectRemoveItemsFile();
	afx_msg void OnBnClickedButtonStart()

private:
	CString m_strSourceFilePath;
	CString m_strSourceFileName;
	CString m_strRemoveFilePath;
	CString m_strRemoveFileName;
	CString m_strOutputFilePath;
	BOOL	m_bSourceFileSelected;
	BOOL	m_bRemoveFileSelected;
}

.cpp

#include <fstream>
void CRemoveSettingsDlg::OnBnClickedButtonSelectSourceFile()
{
	// TODO: Add your control notification handler code here
	CFileDialog fileDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, _T("Txt Files(*.txt;*.*)|*.txt;*.*|| "), this);
	if (fileDlg.DoModal() == IDOK)
	{
		m_strSourceFilePath = fileDlg.GetPathName();
		m_strSourceFileName = fileDlg.GetFileName();
		CString strFolderPath = fileDlg.GetFolderPath();
		CString strFileTitle = fileDlg.GetFileTitle();
		m_strOutputFilePath = strFolderPath + _T("\\") + strFileTitle + _T("_Modify.txt");

		m_bSourceFileSelected = TRUE;
		if (m_bRemoveFileSelected)
		{
			CWnd* pWnd = GetDlgItem(IDC_BUTTON_START);
			pWnd->EnableWindow(TRUE);
		}
		UpdateData(FALSE);
	}
}

void CRemoveSettingsDlg::OnBnClickedButtonSelectRemoveItemsFile()
{
	// TODO: Add your control notification handler code here
	CFileDialog fileDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, _T("Txt Files(*.txt;*.*)|*.txt;*.*|| "), this);
	if (fileDlg.DoModal() == IDOK)
	{
		m_strRemoveFilePath = fileDlg.GetPathName();
		m_strRemoveFileName = fileDlg.GetFileName();

		m_bRemoveFileSelected = TRUE;
		if (m_bSourceFileSelected)
		{
			CWnd* pWnd = GetDlgItem(IDC_BUTTON_START);
			pWnd->EnableWindow(TRUE);
		}
		UpdateData(FALSE);
	}
}

void C_Dialog::OnBnClickedButtonStart()
{
	// TODO: Add your control notification handler code here

	std::ifstream  finSource(m_strSourceFilePath);
	if (!finSource)
	{
		CString strMsg = _T("");
		strMsg.Format(_T("Open the file %s failed!"), m_strSourceFilePath);
		AfxMessageBox(strMsg);
		return;
	}

	std::ifstream  finRemove(m_strRemoveFilePath);
	if (!finRemove)
	{
		CString strMsg = _T("");
		strMsg.Format(_T("Open the file %s failed!"), m_strRemoveFilePath);
		AfxMessageBox(strMsg);
		return;
	}

	std::ofstream foutSettings(m_strOutputFilePath, std::ios_base::out);
	if (!foutSettings)
	{
		CString strMsg = _T("");
		strMsg.Format(_T("Open the file %s failed!"), m_strOutputFilePath);
		AfxMessageBox(strMsg);
		return;
	}
	
	while(finSource)
	{
		BOOL bEqual = FALSE;
		CHAR szSourceBuf[_MAX_PATH + 1] = {0};
		finSource.getline(szSourceBuf, _MAX_PATH);		
		if (szSourceBuf[0] == 0 || szSourceBuf[0] == _T('/'))	
		{
			foutSettings << szSourceBuf << std::endl; // Blank line and mark line output directly.
			continue;
		}

		CString strSourceLine(szSourceBuf);
		strSourceLine.Remove(_T(' '));	// Remove space(' ') in line string.

		if (finSource)
		{	
			while (finRemove)
			{
				CHAR szRemoveBuf[_MAX_PATH + 1] = {0};
				finRemove.getline(szRemoveBuf, _MAX_PATH);
				if (szRemoveBuf[0] == 0 || szRemoveBuf[0] == _T('/'))
				{					
					continue;
				}

				CString strRemoveLine(szRemoveBuf);
				strRemoveLine.Remove(_T(' '));

				CString strSourceItem = strSourceLine.Left(14);	// Get key items: 0xAA, 0xBB, 0xFF, 0x00, //Mark --> 0xAA,0xBB,0xFF
				CString strRemoveItem = strRemoveLine.Left(14);
				if (strRemoveItem.CompareNoCase(strSourceItem) == 0)
				{
					TRACE("Equal.\n");
					TRACE("%s\n", szSourceBuf);
					TRACE("%s\n", szRemoveBuf);
					bEqual = TRUE;
					break;
				}				
			}
			finRemove.clear();	// The file pointer reached the end of file, need clear() before seekg().
			finRemove.seekg(0, std::ios::beg);   // Seek file pointer to file begin.

			if (bEqual)	  // if equal, so not need to output to target file.
			{
				continue; 
			}
			foutSettings << szSourceBuf  << std::endl;   // Output to target file.
		}
	}

	foutSettings.flush();  // Need flush(), otherwise output nothing.

	finSource.close();
	finRemove.close();
	foutSettings.close();

	AfxMessageBox(_T("Remove done."));
}

水平有限,欢迎大神指教~~


后续测试发现,上述OnBnClickedButtonStart()中,最终产生的FileC 最后一行会多出一行空行的问题

原因在于
1. ifstream::getline()在读取到换行符"\r\n"时,会将其去掉,而不存储在提供的szSourceBuf 缓冲区中。
2. 而我在将szSourceBuf缓冲区的内容输出到FileC时,由于需要换行的缘故,手动输出了std::endl;
3. 因此如果SourceFile中最后一行的内容没有"\r\n"时,输出到FileC的内容也会加上std::endl;

修改后的OnBnClickedButtonStart()代码如下:

void CRemoveSettingsDlg::OnBnClickedButtonStart()
{
	// TODO: Add your control notification handler code here

	FILE* fpSource = NULL;
	std::string strSrcFilePath = CT2A(m_strSourceFilePath.GetBuffer());	
	fopen_s(&fpSource, strSrcFilePath.c_str(), "r");
	if (!fpSource)
	{
		CString strMsg = _T("");
		strMsg.Format(_T("Open the file %s failed!"), m_strSourceFilePath);
		AfxMessageBox(strMsg);
		return;
	}

	std::ifstream  finRemove(m_strRemoveFilePath);
	if (!finRemove)
	{
		CString strMsg = _T("");
		strMsg.Format(_T("Open the file %s failed!"), m_strRemoveFilePath);
		AfxMessageBox(strMsg);
		return;
	}

	std::ofstream foutSettings(m_strOutputFilePath, std::ios_base::out);
	if (!foutSettings)
	{
		CString strMsg = _T("");
		strMsg.Format(_T("Open the file %s failed!"), m_strOutputFilePath);
		AfxMessageBox(strMsg);
		return;
	}

	CHAR szSourceBuf[_MAX_PATH + 1] = {0};
	while (fgets(szSourceBuf, _MAX_PATH, fpSource) != NULL)
	{		
		if (szSourceBuf[0] == _T('\n') || szSourceBuf[0] == _T('/'))	
		{
			foutSettings << szSourceBuf; // Blank line and mark line output directly.
			continue;
		}

		CString strSourceLine(szSourceBuf);
		strSourceLine.Remove(_T(' '));	// Remove space(' ') in line string.

		BOOL bEqual = FALSE;
		while (finRemove)
		{
			CHAR szRemoveBuf[_MAX_PATH + 1] = {0};
			finRemove.getline(szRemoveBuf, _MAX_PATH);
			if (szRemoveBuf[0] == 0 || szRemoveBuf[0] == _T('/'))
			{					
				continue;
			}

			CString strRemoveLine(szRemoveBuf);
			strRemoveLine.Remove(_T(' '));

			CString strSourceItem = strSourceLine.Left(14);	// Get key items: 0xAA, 0xBB, 0xFF, 0x00, //Mark --> 0xAA,0xBB,0xFF
			CString strRemoveItem = strRemoveLine.Left(14);
			if (strRemoveItem.CompareNoCase(strSourceItem) == 0)
			{
				bEqual = TRUE;
				break;
			}				
		}
		finRemove.clear();	// The file pointer reached the end of file, need clear() before seekg().
		finRemove.seekg(0, std::ios::beg);   // Seek file pointer to file begin.

		if (bEqual)	  // if equal, so not need to output to target file.
		{
			continue; 
		}
		foutSettings << szSourceBuf;   // Output to target file.
	}

	foutSettings.flush();  // Need flush(), otherwise output nothing.

	fclose(fpSource);
	finRemove.close();
	foutSettings.close();

	AfxMessageBox(_T("Remove done."));
}

主要修改的地方:

1.使用fopen_s()打开文件;
由于
fopen_s( FILE ** _File, In_z const char * _Filename, onst char * _Mode)
第二个参数需要const char* _Filename,而我的m_strSourceFilePath 为CString类型,因此需要进行类型转换:

std::string strSrcFilePath = CT2A(m_strSourceFilePath.GetBuffer());
fopen_s(&fpSource, strSrcFilePath.c_str(), “r”);**

2.使用fgets()按行读取文本内容(包括每行末尾的换行符"\r\n")

参考链接:
https://www.jb51.net/article/143672.htm

CString转换为string:
https://www.cnblogs.com/HappyEDay/p/7016162.html


(fopen_s 和 fstream混编,感觉有点不伦不类,不过问题解决了就行了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值