对于使用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混编,感觉有点不伦不类,不过问题解决了就行了)