文件操作
Windows提供了对文件操作的API函数,而在Windows MFC中,提供了类CFile实现对Windows API主要文件操作的封装,还提供了类CArchive支持串行化机制。
1. Windows API函数实现文件操作
在MS-DOS和非NT内核的16位Windows和Windows95操作系统中,都是用FAT(FIle Allocation Table,文件配置表)文件系统。FAT的一个严重缺点是:当文件被删除并且在同一位置再写入新数据时,它们的片段通常是分散的,这就减慢了读写速度。一般通过磁盘碎片整理将分散的片段在一起。
NTFS是Windows NT操作环境和Windows NT高级服务器网络操作环境的文件系统。NTFS通过长文件名,数据保护和恢复能力以及热定位的容错特征增强了系统的可靠性,并增加了对POSIX需求的支持,消除了FAT和HPFS文件系统的限制,文件大小的限制。
文件操作包括文件创建,读写,保存,复制,查找,锁定,删除,文件基本信息获取等,下面介绍几个比较常用的Windows API文件操作函数
1)文件创建
函数CreateFile用于创建一个文件或者打开一个现有的文件。如果调用成功,函数返回一个文件句柄。应用程序使用文件句柄进行其他的文件操作。
HANDLE CreateFile(
__in LPCWSTR lpFileName, // 被创建或打开的文件名
__in DWORD dwDesiredAccess, // 文件的访问形式,比如读操作是GENERIC_READ,写操作是GENERIC_WRITE
__in DWORD dwShareMode, //文件共享标识
__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性数据结构的指针,常设为NULL
__in DWORD dwCreationDisposition, //标识执行文件的创建或者打开
__in DWORD dwFlagsAndAttributes, //文件的属性和标志
__in_opt HANDLE hTemplateFile // 指向文件属性模板的句柄
);
参数dwCreationDisposition的主要标识
CREATE_NEW 创建一个新的文件,如果文件已存在,则函数调用失败
CREATE_ALWAYS 创建一个文件,如果文件已存在,则该文件重写
OPEN_EXISTING 打开一个文件,如果文件不存在,则函数调用失败
OPEN_ALWAYS 打开一个文件,如果文件不存在,则创建该文件
TRUNCATE_EXISTING 打开一个文件,如果文件不存在,则函数调用失败,如果文件已存在,则将其删为0字节
2)文件读写
函数ReadFile和函数WriteFile分别用于执行文件读取和写入操作
BOOL ReadFile(
__in HANDLE hFile, // 所打开文件的文件句柄
__out_bcount_part_opt(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer, // 指向缓冲区的指针,用于存放从文件中读取的数据
__in DWORD nNumberOfBytesToRead, // 将要读取数据的字节数
__out_opt LPDWORD lpNumberOfBytesRead, // 指向实际读取字节数的指针
__inout_opt LPOVERLAPPED lpOverlapped // 指向OVERLAPPED结构的指针,如果=NULL则从当前位置读取数据
);
BOOL WriteFile(
__in HANDLE hFile, // 所打开文件的文件句柄
__in_bcount_opt(nNumberOfBytesToWrite) LPCVOID lpBuffer, // 指向缓冲区的指针,用于存放写入文件的数据
__in DWORD nNumberOfBytesToWrite, // 将要写入数据的字节数
__out_opt LPDWORD lpNumberOfBytesWritten, // 指向实际写入字节数的指针
__inout_opt LPOVERLAPPED lpOverlapped // 指向OVERLAPPED结构的指针,如果=NULL则从当前位置写入数据
);
3)文件的保存,复制,查找,锁定和删除
在进行文件读写时,几乎所有操作都是在内存中进行,当用户需要将数据写入磁盘时,可以调用函数FlushFileBuffers实现
BOOL FlushFileBuffers(
__in HANDLE hFile // 锁打开文件的句柄,函数返回TRUE,则缓冲区的数据写入磁盘
);
文件复制函数CopyFile
BOOL CopyFile(
__in LPCWSTR lpExistingFileName, // 指向已有文件名称的指针
__in LPCWSTR lpNewFileName, // 指向另存为文件名称的指针
__in BOOL bFailIfExists // 用于指示当与新文件名称相同的文件已存在时,如果=TRUE则函数不执行,如果=FALSE则函数执行并将该文件覆盖
);
函数SearchPath用于根据指定路径查找文件
DWORD SearchPath(
__in_opt LPCWSTR lpPath, // 指向查找路径的指针
__in LPCWSTR lpFileName, // 指向文件名称的指针
__in_opt LPCWSTR lpExtension, // 指向文件扩展名的指针
__in DWORD nBufferLength, // 用来接收文件路径名的缓冲区长度
__out_ecount_part_opt(nBufferLength, return + 1) LPWSTR lpBuffer, // 指向接收文件路径名的缓冲区的指针
__out_opt LPWSTR *lpFilePart // 指向路径名中文件名部分的指针
);
为了向文件写入数据,有时不允许其他进程对文件进行操作,函数LockFile用来锁定文件的作用
BOOL LockFile(
__in HANDLE hFile, // 带锁定文件的句柄
__in DWORD dwFileOffsetLow, // 文件锁定位置的低字节
__in DWORD dwFileOffsetHigh, // 文件锁定位置的高字节
__in DWORD nNumberOfBytesToLockLow, // 锁定长度的低字节
__in DWORD nNumberOfBytesToLockHigh // 锁定长度的高字节
);
不需要的文件时,通过DeleteFile来删除文件
BOOL DeleteFile(
__in LPCWSTR lpFileName // 指向待删除文件名称的指针
);
4)获取文件基本信息
函数GetFileInformationByHandle用来获取文件的基本信息,比如创建时间,最后访问时间等
BOOL GetFileInformationByHandle(
__in HANDLE hFile, // 所要获取信息的文件的句柄
__out LPBY_HANDLE_FILE_INFORMATION lpFileInformation // 指向BY_HANDLE_FILE_INFORMATION结构的指针
);
结构BY_HANDLE_FILE_INFORMATION的定义
typedef struct _BY_HANDLE_FILE_INFORMATION {
DWORD dwFileAttributes; // 文件属性标识
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后访问时间
FILETIME ftLastWriteTime; // 文件最后写入时间
DWORD dwVolumeSerialNumber; // 文件的卷标号码
DWORD nFileSizeHigh; // 文件大小的高字节
DWORD nFileSizeLow; // 文件大小的低字节
DWORD nNumberOfLinks; // 文件的连接号
DWORD nFileIndexHigh; // 文件索引号的高字节
DWORD nFileIndexLow; // 文件索引号的低字节
} BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION, *LPBY_HANDLE_FILE_INFORMATION;
2. 类CFile
MFC通过类CFile提供了对文件和文件操作的支持,他封装了公用的无缓冲二进制文件操作,并提供了基本文件I/O服务,文件定位和文件状态信息服务等,文件类的继承关系
CObject
|---CFile
|---CMemFile
|---COleStreamFile
|---CSocketFile
|---CStudioFile
类CFile的构造函数
CFile();
CFile(HANDLE hFile); // 参数为已打开文件的句柄
CFile(LPCTSTR lpszFileName, UINT nOpenFlags); // 文件路径的字符串/共享和访问模式标识
3. 串行换与类CArchive
在面向对象程序设计中,串行化就是对象的存储和恢复处理过程,即数据存储到磁盘上和从磁盘中读取。
串行换机制,通过对“<<”和“>>”运算符的重载,再借助于CArchive类对象就能够对数据进行串行化写入或并行化加载。
类CArchive是串行化类,是MFC类库中独立的一个类,他不继承于类CObject或其他任何类。基本功能是支持用户将持久性数据以二进制流的形式写入磁盘或者从磁盘中读出。
创建一个支持串行化的类的基本步骤:
A. 创建一个继承于CObject的类
B. 为该类定义默认构造函数(不带参数的构造函数)
C. 在类的头文件中包含宏DECLARE_SERIAL
D. 在类的实现文件中包含宏IMPLEMENT_SERIAL
E. 重载函数Serialize,串行化该类的数据成员
(详细可看MFC类库的组成一章)
类CArchive类对象是附着在CFile类对象上,而且在使用CArchive类对象对文件读写操作时必须确保文件处于打开状态。
串行化虽然简化了数据对象的保存和载入,但串行化必须一次从文件中载入所有数据。