实验目的
(1)熟悉Windows系统文件读\写相关API。
(2)掌握无缓冲方式实现文件读\写相关参数的设置
实验准备知识:相关API函数介绍
-
文件创建
函数CreateFile( ) 用于创建一个新文件,如果文件已经存在,则得到该文件的句柄。该函数的参数dwFalgsAndAttributes决定了文件的传输方式,对于普通的文件传输,可将参数设置为FILE_ATTRIBUTE_NORMAL;而若设置为FILE_FLAG_NO_BUFFERING,表示不使用高速缓存进行文件传输;若同时使用标志FILE_FLAG_NO_BUFFERING和FILE_FLAG_OVERLAPPED,可对文件进行异步传输;若设置为FILE_FLAG_SEQUENTIAL_SCAN,表示使用高速缓存进行文件的传输。
原型:
HANDLE CreateFile(
LPCTSTR lpFileName, //指向文件名的指针
DWORD dwDesiredAccess, //读/写访问模式
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
DWORD dwCreationDisposition, //文件存在标志
DWORD dwFlagsAndAttributes, //文件属性
DWORD hTemplateFile //指向访问模板文件的句柄
) ;
参数说明:
( 1 ) lpFILEName:指向文件名的指针。
( 2 ) dwDesiredAccess:指出访问文件的类型,可以是读访问、写访问、读 / 写访问或查询访问。该参数可以是表4-1中的组合。
( 3 ) dwShareMode:指出文件共享模式。若dwShareMode的值为0,表示目标不能被共享。若要共享文件,可以使用表4-2中的组合。
表4-1 不同值的描述
值 描述
0 查询访问
GENERIC_READ 读访问,从文件中读出数据,且移动文件指针。当需要对文件进行读写时,该属性可以与GENERIC_WRITE组合使用
GENERIC_WRITE 写访问,将数据写入文件,且移动文件指针。当需要对文件进行读/写时,该属性可以与GENERIC_READ组合使用表4-2 dwShareMode的值 值 描述
FILE_SHARE_DELETE 仅当删除访问时,对文件的打开操作才能成功
FILE_SHARE_READ 仅当读访问时,对文件的打开操作才能成功
FILE_SHARE_WRITE 仅当写访问时,对文件的打开操作才能成功
( 4 ) lpSecurityAttributes:指向安全属性的指针。为NULL时,子进程可以继承该安全描述符。
( 5 ) dwCreationDisposition:文件存在标志。指出当文件不存在时,可以对文件进行何种操作。可以取表4-3中的值。
表4-3 dwCreationDisposition 的值
值 描述
CREAT_NEW 创建新文件。若文件已存在,则该函数调用失败
CREAT_ALWAYS 创建新文件。若文件已存在,则该函数覆盖原文件的内容且清空现有属性
OPEN_EXISTING 打开已存在文件,若文件不存在,则该函数打开失败
OPEN_ALWAYS 若文件存在,则打开该文件,若文件不存在,则以CREAT_NEW方式创建文件
TRUNCATE_EXISTING 打开文件,并将文件的大小截取为0
( 6 ) dwFlagsAndAttributes:指出文件属性和标志
除了FILE_ATTRIBUTE_NORMAL属性之外,参数dwFlagsAndAttributes可以取表4-4中任何属性的组合。
表4-4 属性描述
属 性 描述
FILE_ATTRIBUTE_ARCHIVE 文件可以被存档
FILE_ATTRIBUTE_HIDDEN 文件可以被隐藏
FILE_ATTRIBUTE_NORMAL 文件没有其他属性,该属性仅当单独时使用才有效
FILE_ATTRIBUTE_OFFLINE 文件中的数据被脱机存储,文件中的数据不能立即有效
FILE_ATTRIBUTE_READONLY 文件只能读
FILE_ATTRIBUTE_SYSTEM 文件被系统使用
FILE_ATTRIBUTE_TEMPORARY 文件被临时存储
参数dwFlagsAndAttributes还可以取表4-5中任何属性的组合。
表4-5 属性补充
属 性 描述
FILE_FLAG_WRITE_THROUGH 系统对文件的任何写操作,当缓冲的内容改变时立即写回磁盘
FILE_FLAG_OVERLAPPED 异步读/写,使用该属性时,文件指针将不被保留
FILE_FLAG_NO_BUFFERING 文件不使用缓冲
FILE_FLAG_RANDOM_ACCESS 文件随机访问
FILE_FLAG_SEQUENTIAL_SCAN 文件被顺序访问
FILE_FLAG_DELETE_ON_CLOSE 当文件句柄关闭时,文件立即被删除
FILE_FLAG_BACKUP_SEMANTICS 文件用于备份或转储
FILE_FLAG_POSIX_SEMANTICS 文件访问遵循POSIX协议。
( 7 ) hTemplateFile:指向访问模板文件的句柄,可以将其设置为空。
返回值:
文件创建成功,该函数返回文件句柄,否则返回INVALID_HANDLE_VALUE,可调用函数GetLastError( )查询失败的原因。
用法举例:
HANDLE handle;
handle = CreateFile(“nobuffer.txt”,GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,
NULL,NULL);
//使用函数创建一个新文件nobuffer.txt,对该文件只能进行写操作
- 读文件
函数Readfile( ) 从文件指针指示的位置开始读取文件中的数据。如果文件不是用FILE_FLAG_OVERLAPPED属性创建的,则文件指针移动到实际读出字节数所处的位置;如果文件是用FILE_FLAG_OVERLAPPED属性创建的,则文件指针由应用程序来调整其位置。
原型:
BOOL ReadFILE(
HANDLE hFile, //要读的文件的句柄
LPVOID lpBuffer, //指向文件缓冲区的指针
DWORD nNumberOfBytesToRead, //从文件中要读取的字节数
LPDWORD lpNumberOfBytesRead, //指向从文件中要读取的字节数的指针
LPOVERLAPPED lpOverlapped //指向OVERLAPPED结构的指针
) ;
参数说明:
( 1 ) hFile:指向要读的文件的句柄
( 2 ) lpBuffer:指向文件缓冲区的指针。
( 3 ) nNumberOfBytesToRead:从文件中要读取的字节数。
( 4 ) lpNumberOfBytesRead:指向从文件中要读取的字节数的指针。
( 5 ) lpOverlapped:指向OVERLAPPED结构的指针,如果文件是用FILE_FLAG_OVERLAPPED属性创建的,则需要此结构;如果文件是用FILE_FLAG_OVERLAPPED属性打开的,则参数lpOverlapped不为NULL,它指向一个OVERLAPPED结构。如果文件是用FILE_FLAG_OVERLAPPED属性创建且参数lpOverlapped为NULL,则该函数不能正确报告读操作是否完成。
如果文件用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则读操作从OVERLAPPED结构中指定的位置开始,且ReadFile( )函数在读操作完成之前返回。此时ReadFile( )返回FALSE,并且GetLastError( )函数返回ERROR_IO_PENDING,即执行读文件操作的进程被挂起,当读操作完成时,进程才继续执行。在OVERLAPPED结构中指定的事件被设置为读操作完成的发送信号状态。
如果文件不用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则读操作从OVERLAPPED结构中指定的位置开始,操作完成时ReadFile( )函数返回。
返回值:
如果函数调用成功,则返回值为非0值。
如果返回非0值,且读出的字节数为0,则说明执行读操作时文件的指针出界,此时调用GetLastError( )函数,可得到返回值ERROR_HANDLE_EOF。但若文件用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则ReadFile( )函数返回为FALSE。
如果函数调用失败,则返回值为0。若要得到更多的错误信息,可调用函数GetLastError( )。
用法举例:
下面的例子说明如何测试异步读操作的文件结尾。
//设置 OVERLAPPED结构
gOverLapped.Offset =0;
gOverLapped.OffsetHigh=0;
gOverLapped.hEvent =NULL;
//执行异步读操作
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, &gOverlapped);
//异步读不成功,进程挂起
if ( !bResult)
{ //处理错误码
switch (dwError = GetLastError( ))
{
case ERROR_HANDLE_EOF:
{
//文件出界处理
}
case ERROR_IO_PENDING:
{
//异步I/O进行中
//做其他事……
GoDoSomethingElse( );
//检查异步读结果
bResult = GetOverlappedResult( hFile, &gOverlapped,
&nBytesRead, FALSE) ;
//若不成功
if ( !bResult )
{ //处理错误代码
switch (dwError = GetLastError( ) )
{
case ERROR_HANDLE_EOF:
{ //异步操作期间达到文件尾
}
//处理其他错误码
}
}
}// end case
//处理其他错误码
}//end switch
}// end if
3. 写文件
函数WriteFile ( ) 将数据写入文件。函数在文件指针所指的位置完成写操作,写操作完成后,文件指针按实际写入的字节数来调整。
原型:
BOOL WriteFile(
HANDLE hFile, // 要读的文件的句柄
LPVOID lpBuffer, // 指向文件缓冲区的指针
DWORD nNumberOfBytesToWrite, // 从文件中要读取的字节数
LPDWORD lpNumberOfBytesWritten, // 指向从文件中要读取的字节数的指针
LPOVERLAPPED lpOverlapped // 指向 OVERLAPPED结构的指针
) ;
参数说明:
( 1 ) hFile:指向要写文件的句柄。
( 2 ) lpBuffer:指向文件缓冲区的指针。
( 3 ) nNumberOfBytesToWrite:向文件中写入的字节数。
( 4 ) lpNumberOfBytesWritten:向文件中写入的字节数的指针
( 5 ) lpOverlapped:指向OVERLAPPED结构的指针。如果文件是用FILE_FLAG_OVERLAPPED属性打开的,则需要此结构;如果文件是用FILE_FLAG_OVERLAPPED属性打开的,则参数lpOverlapped不为NULL,它指向一个OVERLAPPED结构。如果文件是用FILE_FLAG_OVERLAPPED属性创建且参数lpOverlapped为NULL,则该函数不能正确报告读操作是否完成。
如果文件用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则读操作从OVERLAPPED结构中指定的位置开始,且WriteFile ( )函数在写操作完成之前返回。此时WriteFile ( )返回FALSE,并且GetLastError( )函数返回ERROR_IO_PENGDING,即执行写文件操作的进程被挂起,当写操作完成时,进程才继续执行,在OVERLAPPED结构指定的事件被设置为写操作完成的发送信号状态。
如果文件不用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped为NULL,则写操作从文件当前位置开始,操作完成时WriteFile()函数返回。
如果文件不用FILE_FLAG_OVERLAPPED属性打开且参数lpOverlapped不为NULL,则写操作从OVERLAPPED结构中指定的位置开始,操作完成时WriteFile()函数返回。
返回值:
如果函数调用成功,则返回值为非0值。如果函数调用失败,则返回值为0.若要得到更多的错误信息,可调用函数GetLastError().
4.关闭文件句柄
函数CloseHandle()关闭与文件相关的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使进程安全运行。
原型:
BOOL CloseHandle(
HANDLE hObject
);
参数说明:
hObject:已打开对象的句柄
返回值:
如果函数调用成功,则返回值为非0值。如果函数调用失败,则返回值为0.若要得到更多的错误信息,可调用函数GetLastError()。
实验内容
建立一个函数,使用该函数将源文件source.txt中的内容读出,再写到目标文件nobuffer.txt中去。
代码
// 1.cpp : Defines the entry point for the console application.
//
/#include "stdafx.h"
/#include "1.h"
/#ifdef _DEBUG
/#define new DEBUG_NEW
/#undef THIS_FILE
static char THIS_FILE[]=FILE;
/#endif
DWORD BufferSize=1024;
char buf[1024];
/////////////////////////////////////
//The one and only application object
CWinApp theApp;
using namespace std;
void FileWrite_NoBuffer(charsource,chardestination);
int_tmain(int argc,TCHARangv[],TCHARenvp[])
int nRetCode=0;
printf("Call FileReadWrite_NoBuffer!\n");
FileReadWrite_NoBuffer("source.txt","nobuffer.txt");
return nRetCode;
)
void FileReadWrite_NoBuffer(charsource,chardestination)
{
HANDLE handle_src,handle_dst;
DWORD NumberOfByteWrite;
BOOL cycle;
char*buffer;
buffer=buf;
//创建文件
handle_src=CreateFile(source,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING,
NULL);
handle_dst=CreateFile(destination,
GENERIC_WRITE,
NULL,
NULL,
CREATE_ALWAYS,
NULL,
NULL);
if (handle_src==INVALID_HANDLE_VALUE||
handle_dst==INVALID_HANDLE_VALUE)
{
printf("File Create Fail!\n");
exit(1);
}
cycle=TURE;
while(cycle)
{
NumberOfByteRead=BufferSize;
//读文件
if(!ReadFile(handle_src,buffer,NumberOfByteRead,&NumberOfByteRead,NULL))
{
printf("Read File Error!%d\n",GetLastError());
exit(1);
}
if(NumberOfByteRead
if(!WriteFile(handle_dst,buffer,NumberOfByteRead,&NumberOfByteWrite,NULL))
{
printf("Write File Error!%d\n",GetLastError());
exit(1);
}
}
//关闭文件句柄
CloseHandle(handle_src);
CloseHandle(handle_dst);
}
实验总结
该实验完成无缓冲方式的文件读/写操作。先创建两个文件,source.txt和nobuffer.txt,然后反复从文件source.txt中读出数据块,并写到文件nobuffer.txt中去,直到文件尾为止。