CFile类
信息源自官方文档:https://learn.microsoft.com/zh-cn/cpp/mfc/reference/cfile-class?view=msvc-170。
CFile是Microsoft 基础类文件类的基类。它直接提供非缓冲的二进制磁盘输入/输出设备,并直接地通过派生类支持文本文件和内存文件。CFile与CArchive类共同使用,支持MFC对象的串行化。
CFile采用API函数做内核:
1.Open()
virtual BOOL Open(
LPCTSTR lpszFileName,
UINT nOpenFlags,
CFileException* pError = NULL);
virtual BOOL Open(
LPCTSTR lpszFileName,
UINT nOpenFlags,
CAtlTransactionManager* pTM,
CFileException* pError = NULL);
a. lpszFileName
一个包含所需文件的路径的字符串。 该路径可以是相对路径、绝对路径或网络名称 (UNC)。
b.nOpenFlags
用于定义文件的共享和访问模式的 UINT。 它指定打开文件时要执行的操作。 可以使用按位“或”(|) 运算符来组合选项。 一个访问权限和一个共享选项是必需的;modeCreate 和 modeNoInherit 模式是可选的。 有关模式选项的列表,请参阅 CFile 构造函数。
c.pError
指向接收失败操作状态的现有文件异常对象的指针。
d.pTM
指向 CAtlTransactionManager 对象的指针
这里附带说一下,我们在Windows文件中常会看见一种叫文件句柄的东西,其定义如下:
在文件I/O中,要从一个文件读取数据,应用程序首先要调用操作系统函数并传送文件名,并选一个到该文件的路径来打开文件。该函数取回一个顺序号,即文件句柄(file handle),该文件句柄对于打开的文件是唯一的识别依据。要从文件中读取一块数据,应用程序需要调用函数ReadFile,并将文件句柄在内存中的地址和要拷贝的字节数传送给操作系统。当完成任务后,再通过调用系统函数来关闭该文件。
这里常见的参数其实就第一和第二个参数,你有路径,有打开方式,打开一个文件就不是问题。
打开方式(nOpenFlags):
CFile::modeCreate 文件以创建形式打开(创建一个新的文件,老文件数据将会丢失 ,可以配合CFile::modeNoTruncate 一起使用,不会覆盖老文件)
CFile::modeRead 采用读取方式打开文件
CFile::modeWrite 采用写入方式打开文件
CFile::modeReadWrite 采用读写方式打开文件
CFile::modeNoTruncate 采用不截断形式打开文件(与CFile::modeCreate配合使用在文件存在的情况下不会创建新的文件, 与CFile::modeWrite配合使用在文件有数据的情况下写入数据不会清空文件数据(会存在数据覆盖现象,下面会讲文件写入位置设置方法))
CFile::modeNoInherit 防止这个将要被打开的文件来源于子进程(实际几乎用不到)
CFile::typeBinary 文件数据将会以二进制的形式显示
CFile::shareDenyNone 采用共享形式打开文件(这个文件在不做读写的情况下打开)
函数内部调用API:
CreateFile(lpszFileName, dwAccess, dwShareMode, &sa, dwCreateFlag, dwFlags, NULL);
2.GetLength()
int nlen = file_.GetLength();
//获取文件的当前总长度(读或写文件时可以随时调用函数查看文件的长度)方法
函数内部调用API:
GetFileSize(m_hFile, &liSize.HighPart);
3.Read()
char GetStr[4096] = {0};
file_.Read(GetStr,4096);
//读取文件(串口)方法
函数内部调用API:
ReadFile(m_hFile, lpBuf, nCount, &dwRead, NULL);
//内部采用同步读取方式(在串口读取方面我更喜欢异步方式,相关参考我的API串口异步文章)
在官方文档中描述为:
virtual UINT Read(
void* lpBuf,
UINT nCount);
4.Write()
char GetStr[4096] = {0};
memcpy(GetStr,"123yui",sizeof(char) * 6);
file_.Write(GetStr,4096);
//写入文件(串口)方法
函数内部调用API:
WriteFile(m_hFile, lpBuf, nCount, &nWritten, NULL);
5.Close()
file_.Close();
//文件关闭方法(不需要操作这个打开文件时随手释放文件句柄)
函数内部调用API:
CloseHandle(m_hFile);
这里如果都是内部使用的话,全部都是基于C的。没有UNICODE编码,情况会比较简单。当我们需要用到MFC问题时,会引入宽字符的CString变量,此时使用CStdioFile这个子类会更好。
CStdioFile类
信息源自官方文档:https://learn.microsoft.com/zh-cn/cpp/mfc/reference/cstdiofile-class?view=msvc-170。
它时CFile的子类,比较关键的是它对READ、SEEK、WRITE这三个函数进行了重写。
它可以执行readstring函数,直接提取出LPTSTR类型的CString来。
virtual LPTSTR ReadString(
LPTSTR lpsz,
UINT nMax);
virtual BOOL ReadString(CString& rString);
这里的读取是一行一行的读,比起CFile来说就灵活的多了。
实战案例
我们准备如下MFC程序:
准备如此一个CSV文件:
在HIT按钮下添加如下指令
void CMFCCSVLABDlg::OnBnClickedHit()
{
// TODO: 在此添加控件通知处理程序代码
CFile file;
file.Open(_T("./test.csv"), CFile::modeRead);
char buf[1024];
file.Read(buf,1024);
CString str(buf);
MessageBox(str,_T("str"));
file.Close();
CStdioFile CSFile;
CSFile.Open(_T("./test.csv"), CFile::modeRead);
CString C_buf_1;
CSFile.ReadString(C_buf_1);
MessageBox(C_buf_1, _T("C_buf_1"));
CString C_buf_2;
CSFile.ReadString(C_buf_2);
MessageBox(C_buf_2, _T("C_buf_2"));
CSFile.Close();
}
可以看到把两种不同的读取方式都试过了,实际的结果为:
可以看到结论已经很清晰了。怎么样读取数据,就这么简单。