在Windows系统中,创建和打开文件都是使用API函数CreateFile,CreateFile通过指定不同的参数来表示是新建一个文件,打开已经存在的文件,还是重新建立文件等。读写文件最为直接的方式是使用ReadFile和WriteFile函数,也可以使用文件镜像,获取文件大小一般使用GetFileSize函数,也可以使用GetFileAttributesEx等函数(在上节介绍)。读写文件、获取文件大小之前都需要使用CreateFile创建或打开的文件,获得文件句柄。
在文件操作中,文件句柄是一个关键的概念。文件句柄惟一标识了一个文件,ReadFile、WriteFile、GetFileSize等函数是使用文件句柄作为参数来表示,用户需要读、写、获取大小的文件是哪一个文件。在对文件进行操作前,都必须要使用CreateFile获得文件句柄。
(1)CreateFile
CreateFile是文件操作中最主要的一个函数。几乎所有的文件操作都需要使用到文件句柄。而CreateFile函数为这些操作建立文件句柄。CreateFile函数的参数多,而且比较复杂,涉及文件名、文件的共享模式、存取方式、操作模式、权限、标志和文件属性等。每一个参数都可以有多种值可供选择,代表了不同的意义。
◇参数
lpFileName:输入参数,操作对象文件的相对路径或绝对路径。
dwDesiredAccess:输入参数,指明对文件对象的操作存取方式,可以是GENERIC_READ,表示需要读文件;可以是GENERIC_WRITE,表示需要写文件;也可以是GENERIC_READGENERIC_WRITE,表示既可以读也可以写。
dwShareMode:输入参数,共享模式。指明与其他进程是否共享该文件,可以是共享读(FILE_SHARE DELETE)、共享写(FILE_SHARE_WRITE)、共享删除(FILE_SHARE.READ),如果要指明多个属性,使用“位与~‘I”运算。如果指明上述参数,其他进程就可以对文件进行相关操作。如果本进程需要独占本文件,则将本参数设置为0。
lpSecurityAttributes:指向SECURITY_ATTRIBUTES结构的指针,表示本文件句柄的安全属性,能影响其是否可被子进程继承等操作。如果设定为NULL,则子进程不可继承本句柄。SECURITY ATTRIBUTES结构不常用,对此数据结构的设置,涉及Windows系统中对权限管理的原理,在本章中不作详细说明。
dwCreationDisposition:输入参数,操作模式。
dwFlagsAndAttributes:输入参数,文件属性和文件标志
一般情况下文件属性较常用,而操作标志不常用,可以使用“1”运算符指定多个属性和标志。
hTemplateFile:输入参数,当存取权限包括GENERIC_WRITE时,可以设置为一个模板文件的句柄。一般情况下,可设置为NULL,表示不使用模板文件。
◇返回值
返回HANDLE数据类型值,表示文件的句柄,如果返回INVALID_HANDLE_VALUE,表示操作失败。
(2)ReadFile。
ReadFile动能是从文件中读出数据。需要使用CreateFile所返回的文件句柄。
◇参数
hFile:输入参数,读取数据的文件对象,由CreateFile创建,调用CreateFile打开文件时需要指明GENERIC_READ读取操作模式。
lpBuffer:输入参数,指向读取文件数据存储的内存缓冲区。
nNumberOfBytesToRead:输入参数,指明需要从文件中读出的数据的大小,不能大于lpBuffer内存块的大小,否则会造成溢出。
lpNumberOfBytesRead:输出参数,指向存储实际读出的数据大小的DWORD变量。在读文件时,可能由于已经到达文件尾等原因,其值可能小于rtNumberOfBytesToRead。如果lpOverlapped为NULL,则该参数不能为NULL。
lpOverlapped:输入参数,指向OVERLAPPED结构体的指针,如果调用CreateFile时设置了FILE_FLAG_OVERLAPPED标志,则需要使用该参数,否则可以为NULL。
◇返回值
返回BOOL值,表示读文件是否成功。
◇使用说明
在读的过程中,文件指针会随着读操作的进行而自动移动,在循环调用本函数时,会顺序读出文件的内容,如果程序返回失败,可以使用GetLastError函数获取错误信息。
(3)WriteFile。
WriteFile函数的功能是将数据写入到文件中,写入到文件指针所在的位置,写入操作完成后,文件指针会移动到写入的数据之后
◇参数
hFile:输入参数,写入数据的文件对象,由CreateFile创建,调用CreateFile打开文件时,需要指明GENERIC_WRITE读取操作模式。
lpBuffer:输入参数,指向需写入文件数据存储的内存缓冲区。
nNumberOBytesToWrite:输入参数,指明需要写入文件中的数据的大小。
lpNumberOiBytesWritten:输出参数,指向存储真实写入的数据大小的变量,可能由于已经到达文件尾等原因,其值可能与nNumberOfBytesToWrite不同。如果lpOverlapped为NULL,则该参数不能为NULL。
lpOverlapped:输入参数,指向OVERLAPPED结构体的指针,如果调用CreateFile时设置了FILE_FLAG_OVERIAPPED标志,则需要使用该参数。可以为NULL。
◇返回值
返回BOOL值,表示写文件是否成功。
◇使用说明
在读的过程中,文件指针会随着写操作的进行而移动,在循环调用本函数时,会按顺序写入文件内容。如果程序返回失败,可以使用 GetLastError函数获取错误信息。
(4)GetFileSize、GetFileSizeEX.
GetFileSize、GetFileSizeEX的功能是一致的,都是获取文件大小
◇参数
GetFileSize的参数如下。
hFile:输入参数,读取数据的文件对象,由CreateFile创建,调用CreateFile打开文件时需要指明GENERIC_READ读取操作模式或者GENERIC_WRITE写入操作模式。
lpFileSizeHigh:输出参数,表示得到的文件大小的高32位。该参数可以为NULL,为NULL时表示文件大小可以用DWORD表示。具体解释请参考返回值部分。
GetFileSizeEx的参数如下。
hFile:输入参数,读取数据的文件对象,由CreateFile创建,调用CreateFile打开文件时,需要指明GENERIC_READ读取操作模式或者GENERIC_WRITE写入操作模式。
lpFileSize:输出参数,指向储存文件大小的一个LARGE_ INTEGER联合体,前面已经介绍过了LARGE.1NTEGER结构的相关内容,参见API 9 小节关键数据结构部分。
◇返回值
GetFileSize成功时,返回值为一个表示文件大小DWORD值。
GetFileSize失败时,如果lpFileSizeHigh为NULL,返回INVALID_ FILE_SIZE并可以调用GetLastError函数获取更详细的错误信息;如果lpFileSizeHigh为非NULL,那么同样返回INVALID_FILE_SIZE并且尝试读取lpFileSizeHigh指向的值放在高32位,并将其和返回的低32位DWORD连起来成为一个64位的值来表示文件大小。lpFileSizeHigh为合法值则GetLastError返回NO_ERROR,表示该API仍然成功;否则,GetLastError返回一个不等于NO_ERROR的值。
GetFileSizeEx直接返回BOOL值,表示读文件是否成功。
◇使用说明
实际上这两个函数的区别就在于,对文件大小超出DWORD的情况采取不同处理方式。由于历史原因,GetFileSize用两个32位值来分别储存64位文件大小的高位和地位;而GetFileSizeEx直接将文件大小储存在64位的联合体中。
>>>本实例使用CreateFile、ReadFile、WriteFile来完成创建、打开、读写文件的功能。笔者在实例中自己编写一个SaveDataToFile函数在D盘根目录下建立一个show.txt并向文件中写入数据,并编写了一个ReadFileContent函数读取文件的内容。
1 #include <Windows.h> 2 #include <stdio.h> 3 4 DWORD ReadFileContent(LPSTR szFilePath) 5 { 6 //文件大小 7 HANDLE hFileRead; 8 //保存文件大小 9 LARGE_INTEGER liFileSize; 10 //成功读取的文件数据大小 11 DWORD dwReadedSize; 12 //累加计算已经读取的数据的大小 13 LONGLONG liTotalRead=0; 14 //文件数据缓存 15 BYTE lpFileDataBuffer[32]; 16 17 //打开已经存在的文件,读取内容 18 hFileRead = CreateFileA(szFilePath,//name 19 GENERIC_READ, //以读方式打开 20 FILE_SHARE_READ, //可共享读 21 NULL, //默认安全设置 22 OPEN_EXISTING, //只打开已经存在的文件 23 FILE_ATTRIBUTE_NORMAL, //常规文件属性 24 NULL); //无模板 25 //打开文件是否成功 26 if(hFileRead==INVALID_HANDLE_VALUE) 27 { 28 printf("打开文件失败: %d",GetLastError()); 29 } 30 if(!GetFileSizeEx(hFileRead,&liFileSize)) 31 { 32 printf("获取文件大小失败: %d",GetLastError()); 33 } 34 else 35 { 36 printf("文件大小为: %d\n",liFileSize.QuadPart); 37 } 38 //循环读取并打印内容 39 while(TRUE) 40 { 41 DWORD i; 42 if(!ReadFile(hFileRead,//读取文件句柄 43 lpFileDataBuffer, //存储读取文件内容 44 32, //读取的大小(字节) 45 &dwReadedSize, //实际读取的大小 46 NULL)) //不使用Overlapped 47 { 48 printf("读取文件错误: %d\n",GetLastError()); 49 break; 50 } 51 printf("读取了%d字节,文件内容是: ",dwReadedSize); 52 53 for(i=0;i<dwReadedSize;i++) 54 { 55 printf("0x%x ",lpFileDataBuffer[i]); 56 } 57 printf("\n"); 58 liTotalRead+=dwReadedSize; 59 if(liTotalRead==liFileSize.QuadPart) 60 { 61 printf("读取文件结束\n"); 62 break; 63 } 64 } 65 CloseHandle(hFileRead); 66 return 0; 67 } 68 69 DWORD SaveDataToFile( 70 LPSTR szFilePath, 71 LPVOID lpData, 72 DWORD dwDataSize) 73 { 74 //文件句柄 75 HANDLE hFileWrite; 76 //成功写入的数据大小 77 DWORD dwWritedDateSize; 78 //打开已经存在的文件,读取内容 79 hFileWrite=CreateFileA(szFilePath,//要打开的文件名 80 GENERIC_WRITE,//以写方式打开开 81 0,//可共享读 82 NULL,//默认安全设置 83 OPEN_ALWAYS,//打开已经存在的文件 84 FILE_ATTRIBUTE_NORMAL,//常规属性打开 85 NULL);//无模板 86 //判断是否成功打开 87 if(hFileWrite==INVALID_HANDLE_VALUE) 88 { 89 printf("打开文件失败: %d\n",GetLastError()); 90 } 91 //设置文件指针到文件为 92 SetFilePointer(hFileWrite,0,0,FILE_END); 93 //将数据写入文件 94 if(!WriteFile(hFileWrite,lpData,dwDataSize,&dwWritedDateSize,NULL)) 95 { 96 printf("写文件失败: %d\n",GetLastError()); 97 } 98 else 99 { 100 printf("写文件成功,写入%d字节。\n",dwWritedDateSize); 101 } 102 CloseHandle(hFileWrite); 103 return 0; 104 } 105 int main() 106 { 107 LPSTR szFileData="这是一个例子"; 108 SaveDataToFile("D:\\show.txt",szFileData,lstrlenA(szFileData)); 109 ReadFileContent("D:\\show.txt"); 110 return 0; 111 }