文件IO主要为了 支持文档的存储和加载。
多数MFC程序用CArchive对象实现磁盘文档的存储和加载。
1.CFile:
m_hFile 保存着与CFile相关联的文件的句柄。
m_strFileName 文件名称
GetFilePath // 获得完整文件名
GetFileName // 不含路径的文件名
GetFileTitle // 不含路径,不含后缀
CFile file;
CFileException fe;
if(file.Open(_T("File.txt"), CFile::modeReadWrite, &fe))
{
//
}
else
{
fe.ReportError();
}
CFile file;
try
{
file.Open(_T("File.txt"), CFile::modeReadWrite);
...
}
catch(CFileException *e)
{
e->ReportError();
e->Delete();
}
modeReadWrite
modeRead
modeWrite
modeCreate
modeNoTruncate
shareDenyNone
shareDenyRead
shareDenyWrite
shareDenyExclusive // 禁止其它地方读写
Close// 关闭。CFile析构函数中会自动调用。
BYTE buffer[0x1000];
try
{
CFile file(_T("File.txt"), CFile::modeRead);
DWORD dwBytesRemaining = file.GetLength();
while(dwBytesRemaining)
{
DWORD dwPosition = file.GetPosition();
UINT nBytesRead = file.Read(buffer, sizeof(buffer));
::CharLowerBuffer((LPTSTR)buffer, nBytesRead);
file.Seek(dwPosition, CFile::begin);
file.Write(buffer, nBytesRead);
dwBytesRemaining -= nBytesRead;
}
}
catch(CFileException * fe)
{
e->ReportError();
e->Delete();
}
SeekToBegin
SeekToEnd
Rename
Remove
如文件IO中发生错误,Read,Write或其它CFile函数,发出CFileException异常。
2.CStdioFile
CStdioFile派生自CFile,添加了两个成员函数:
ReadString// 读取当前文件位置到下一个回车符间所有数据
WriteString// 写入字符串。字符串内容写入后,额外在其后添加 0x0D和0x0A。
按行读写
对CStdioFile,一行正文由 回车符(0x0D)和换行符(0x0A)定界的字符串。
try
{
CString string;
CStdioFile file(_T("File.txt"), CFile::modeRead);
while(file.ReadString(string))
{
...
}
}
catch(CFileException *e)
{
e->ReportError();
e->Delete();
}
3.文件枚举
::FineFirstFile// 失败返回INVAILD_HANDLE_VALUE
::FineNextFile//成功返回非NULL
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
TCHAR cFileName[MAX_PATH];// 完整文件名
TCHAR cAlternateFileName[14];
} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;
常用的dwFileAttributes:
FILE_ATTRIBUTE_DIRECTORY 是目录
FILE_ATTRIBUTE_HIDDEN 隐藏的
FILE_ATTRIBUTE_NORMAL 普通的
FILE_ATTRIBUTE_READONLY 只读的
FILE_ATTRIBUTE_TEMPORARY 临时的
文件尺寸:
( nFileSizeHigh * ( MAXDWORD+1)) + nFileSizeLow
void EnumrateFolders()
{
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(_T("*.*"), &fd);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CString name = fd.cFileName;
if(name != _T(".") && name != _T(".."))
{
::SetCurrentDirectory(fd.cFileName);
EnumerateFolders();
::SetCurrentDirectory(_T(".."));
}
}
}
while(::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
}
4.串行化和CArchive类
大部分MFC应用用CArchive对象完成读写工作,而CArchive对象转而利用CFile实现文件IO。
4.1.串行化基础
CArchive ar(&file, CArchive::store);
ar << a << b;
CArchive ar(&file, CArchive::Load);
ar >> a >> b;
已具备串行化支持的类
类 |
---|
基本数据类型 |
CString |
CTime |
CTimeSpan |
COleVariant |
COleCurrency |
COleDataTime |
COleDataTimeSpan |
CSize |
CPoint |
CRect |
SIZE |
POINT |
RECT |
数据串行化或并行化错误时,MFC会发送一个异常。异常类型取决于错误性质。
CMemoryException
CFileException
CArchiveException
编写可串行化类
a.直接或间接得到CObject的派生类
b.在类声明中写入,DECLARE_SERIAL宏,实现中写入,IMPLEMENT_SERIAL宏。
重载Serialize
c.派生类须有默认构造函数
class CLine : public CObject
{
DECLARE_SERIAL(CLine)
protected:
CPoint m_ptFrom;
CPoint m_ptTo;
COLORREF m_clrLine;
public:
CLine(){}
CLine(CPoint from, CPoint to)
{
m_ptFrom = from;
m_ptTo = to;
}
void Serialize(CArchive & ar);
};
IMPLEMENT_SERIAL(CLine, CObject, 2 | VERSIONABLE_SCHEMA)
void CLine::Serialize(CArchive & ar)
{
CObject::Serialize(ar);
if(ar.IsStoring())
{
ar << m_ptFrom << m_ptTo << m_clrLine;
}
else
{
UINT nSchema = ar.GetObjectSchema();
switch(nSchema)
{
case 1:
ar >> m_ptFrom >> m_ptTo;
m_clrLine = RGB(0, 0, 0);
break;
case 2:
ar >> m_ptFrom >> m_ptTo >> m_clrLine;
break;
default:
AfxThrowArchiveException(CArchiveException::badSchema);
break;
}
}
}
在读回数据时,MFC将档案中记录的模式号和应用程序使用的该类的模式号比较,不匹配时,发送CArchiveException异常。m_cause为CArchive::badSchema。
可视化模式是包含 了 VERSIONABLE_SCHEMA标志的模式号。
此标志存在时,MFC发现版本号不匹配时,不产生CArchiveException异常。
串行化/并行化工作过程
// DWORD举例
CArchive& CArchive::operator<<(DWORD dw)
{
if(m_lpBufCur + sizeof(DWORD) > m_lpBufMax)
{
Flush();
}
if(!(m_nMode & bNoByteSwap))
{
_AfxByteSwap(dw, m_lpBufCur);
}
else
{
*(DWORD*)m_lpBufCur = dw;
}
m_lpBufCur += sizeof(DWORD);
return *this;
}
// 举例CString
CArchive& AFXAPI operator<<(CArchive &ar, const CString &string)
{
if(string.GetData()->nDataLength < 255)
{
ar << (BYTE)string.GetData()->nDataLength;
}
ar.Write(string.m_pchData, string.GetData()->nDataLength * sizeof(TCHAR));
return ar;
}
// CObject*
_AFX_INLINE CArchive& AFXAPI operator<<(CArchive & ar, const CObject* pOb)
{
ar.WriteObject(pOb);// 最终调用对象的Serialize来串行化对象的数据成员
return ar;
}
CArchive::Write将定量数据复制到档案的内部缓冲区。
MFC处理了UNICODE和ANSI字符的转换,即存入的UNICODE字符可以ANSI形式读出。反之,亦可。
写入档案的首个类对象:
新类标志: 0xFFFF
模式编号:0x????
类名的字符数:0x????
类名:0x????
类的数据成员:
写入档案的非首个类对象:
旧类标志:0x8000// 最高位为1,低15位为类的索引号
类的数据成员: