Windows提供了一类API来读、写和管理磁盘文件。
使用API函数读写文件时,首先要使用CreateFile函数创建文件对象,调用成功会返回文件句柄。我们以此句柄为参数调用ReadFile和WriteFile函数,进行实际的读写操作;最后调用CloseHandle函数关闭不再使用的文件对象句柄。
1)打开和关闭文件:
CreateFile是一个功能强大的函数,Windows下的底层设备几乎都由它打开。它可以创建或打开文件、目录、物理磁盘、控制台缓冲区、油槽或管道等:
HANDLE CreateFile(
LPCTSTR lpFileName, //要创建或打开的对象的名称
DWORD dwDesiredAccess, //文件的存取方式
DWORD dwShareMode, //共享属性
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //安全属性
DWORD dwCreationDisposition, //文件存在或不存在时系统采取的行动
DWORD dwFlagsAndAttributes, //新文件的属性
HANDLE hTemplateFile //一个文件模板的句柄
);
lpFileName—如果是打开文件,直接指定文件名称即可;如果操作对象是第一个串口,则指定“COM1”为文件名,然后就可以像操作文件一样操作串口了;如果要打开本地电脑上的一个服务,要以//./服务名称为文件名,其中”.”代表本地机器;如果要打开网络中其他主机的文件,则以//主机名/共享目录名/文件名为文件名。
dwDesiredAccess—GENERIC_READ(只读)、GENERIC_WRITE(只写)、两者组合。
dwShareMode—表示文件打开后是否允许其他代码以某种方式再次打开这个文件,可以是下面的组合:
0—不允许文件再被打开。C语言中的fopen函数就是这种方式;
FILE_SHARE_DELETE—允许其他程序代码删除文件;
FILE_SHARE_READ—允许其他程序代码以读方式打开文件;
FILE_SHARE_WRITE—允许其他程序代码以写方式打开文件;
dwCreationDisposition—指定当文件已存在或不存在时系统采取的动作:
CREATE_ALWAYS—创建新文件,若文件存在,则覆盖它,清除存在的属性;
CREATE_NEW—创建新文件。若文件存在,函数执行失败;
OPEN_ALWAYS—如果文件存在则打开它,不存在则创建新文件;
OPEN_EXISTING—打开存在的文件。若文件不存在,函数执行失败;
TRUNCATE_EXISTING—打开文件并将文件截断为0,当文件不存在时函数执行失败。
dwFlagsAndAttributes—指定新建文件属性和标志,可以是以下各项的组合:
FILE_ATTRIBUTE_ARCHIVE—标记归档属性;
FILE_ATTRIBUTE_HIDDEN—标记隐藏属性;
FILE_ATTRIBUTE_READONLY—标记只读属性;
FILE_ATTRIBUTE_SYSTEM—标记系统属性;
FILE_ATTRIBUTE_TEMPORARY—临时文件,操作系统会尽量把所有文件的内容保持在内存中以加快存取速度。使用完后要尽快将它删除。
FILE_FLAG_DELETE_ON_CLOSE—文件关闭后系统立即自动删除它;
FILE_FLAG_OVERLAPPED—使用异步读写文件的方式;
FILE_FLAG_WRITE_THROUGH—系统不会对文件使用缓冲,文件的任何改变都会被系统立即写入硬盘。
hTemplateFile—指定一个文件模板句柄。系统会复制文件模板的所有属性到当前创建的文件中。
打开或创建文件成功时,函数返回文件句柄,失败时返回INVALID_HANDLE_VALUE。
2)移动文件指针:
SetFilePointer函数:系统为每个打开的文件维护一个文件指针,指定对文件的下一个读写操作从什么位置开始。随着数据的读出或写入,文件指针也随之移动。当文件刚被打开时,文件指针处于文件的头部。有时候需要随机读取文件内容,这就需要先调整文件指针:
DWORD SetFilePointer(
HANDLE hFile, //文件句柄
LONG lDistanceToMove, //要移动的距离
PLONG lpDistanceToMoveHigh, //移动距离的高32位,一般位置为NULL
DWORD dwMoveMethod //移动的模式
);
dwMoveMethod参数指明从什么地方开始移动:
FILE_BEGIN—开始移动的位置为0,即从文件头部开始移动;
FILE_CURRENT—开始移动位置是文件指针的当前值;
FILE_END—开始移动位置是文件的结尾,即从文件尾开始移动。
文件指针也可以移动到所有数据后面,比如现在文件长度是200KB,但可以成功地将文件指针移动到1000KB的位置。这样做可以达到扩展文件长度的目的。
SetEndOfFile函数就可以达到截断或者扩展文件的功能。该函数移动指定文件的结束标志(EOF)到文件指针指向的位置。如果文件扩展,旧的EOF位置和新的EOF位置间的内容是未定义的:
BOOL SetEndOfFile(HANDLE hFile);
截断或者扩展文件时,要首先调用SetFilePointer移动文件指针,然后再调用SetFilePointer函数设置新的文件指针位置为EOF。
3)读写文件:
ReadFile和WriteFile函数既可以同步读写文件,也可以异步读写文件;而函数ReadFileEx和WriteFileEx只能异步读写文件。
BOOL ReadFile(
HANDLE hFile, //文件句柄
LPVOID lpBuffer, //指向一个缓冲区,函数会将读出的数据返回到这里
DWORD nNumberOfBytesToRead, //要求读入的字节数
LPDWORD lpNumberOfBytesRead, //指向一个DWORD类型的变量,用于返回实际读入的字节数
LPOVERLAPPED lpOverlapped //一般设为NULL
);
BOOL WriteFile(
HANDLE hFile, //文件句柄
LPVOID lpBuffer, //指向一个缓冲区,包含了要写入文件的数据
DWORD nNumberOfBytesToWrite, //要求写入的字节数
LPDWORD lpNumberOfBytesWritten, //指向一个DWORD类型的变量,用于返回实际写入的字节数
LPOVERLAPPED lpOverlapped //一般设为NULL
);
使用WriteFile写文件时,写入的数据通常被Windows暂存在内部的高速缓存中,等合适的时候再一并写入磁盘。可以使用FlushFileBuffers函数来强制系统清空缓冲区:
BOOL FlushFileBuffers(HANDLE hFile);
4)锁定文件:
当我们对文件数据的一致性要求较高时,为了防止程序在写入过程中其他进程刚好在读取写入区域的数据,可以对已打开文件的某个部分加锁,加锁和解锁的函数分别是LockFile和UnlockFile:
BOOL LockFile(
HANDLE hFile, //文件句柄
DWORD dwFileOffsetLow, //加锁的开始位置
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToLockLow, //加锁区域的大小
DWORD nNumberOfBytesToLockHigh
);
BOOL UnlockFile(
HANDLE hFile, //文件句柄
DWORD dwFileOffsetLow, //解锁的开始位置
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToLockLow, //解锁区域的大小