📄 teach_sp_52.htm
字号:
<!-- LANGUAGE='JavaScript'>write_body();<-->
<!-- LANGUAGE='JavaScript'>write_bar();<-->
<table width=98% cellspacing="0" cellpadding="0" align=center><!--整体框架-->
<tr><td>
<table border=0 width="100%" cellspacing="0" cellpadding="2"><!--标记放置区域-->
<tr>
<td width="30%" align="center" bgcolor="#8E8E8E" valign=middle><img src=../../img/brand_200_60.gif width=200 height=60 alt="LOGO1"></td>
<td width="70%" align="center" bgcolor="#8E8E8E" valign=middle><!-- LANGUAGE='JavaScript'>write_ban();<--></td>
</tr>
<tr>
<td colspan="2" bgcolor="#939393" align=center><font color=white>您当前位置</font> <a href=../../index.htm><font color=white>首页</font></a> <a href=../index.htm><font color=white>开发教程</font></a> <a href=index.htm><font color=white><font class=engul>Visual C++/MFC</font>专题讲座</font></a> <font color=white>5.2 磁盘文件的正常读写与异步读写</font> <font color=white><!-- LANGUAGE='JavaScript'>write_command();<--></font></td>
</tr>
</table><!--标记放置区域 END-->
<table border=0 width=100% cellspacing="0" cellpadding="0">
<tr bgcolor="#F4F4F4">
<td><!-- article title begin here-->
<br>
<p align=center><big>5.2 磁盘文件的正常读写与异步读写</big>
<p>在Win32系统下文件可以支持平常的同步读写和异步读写(但在Win9X下,Win32系统不支持磁盘文件的异步读写)。本节在后面部分将会介绍<a href=#overlap>文件的异步读写</a>,最后一段内容将向大家讲解一下文件的<a href=#lock>区域加锁</a>。
<p>在Win32系统中支持64位长度的文件,所以在很多文件操作函数中需要两个DWORD参数来表示文件长度,一个DWORD用来表示低32位,另一个用来表示高32位。
<p>文件的读写进行在文件被正确打开后,但请确认在打开文件时设置了正确的读写标记。在Win32的文件操作中没有了以前类似与以前ANSI C中的fputs fgets fprintf fscanf等函数,只有类似于fread和fwrite的ReadFile和WriteFile函数。
<p>ReadFile用于文件读,函数原型为:
<pre>
BOOL ReadFile(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped // overlapped buffer
);
</pre>
其中各项参数的含义为:
<ul><li>hFile:文件句柄,为CreateFile时返回的句柄
<li>lpBuffer:保存读入的数据的指针
<li>nNumberOfBytesToRead:指定需要读入的字节数
<li>lpNumberOfBytesRead:返回实际读入的字节数
<li>lpOverlapped:在文件异步读写时使用的数据,在同步读写中全部都设置为NULL,在Win9X中只支持对串口的异步操作。
</ul>
如果返回值为FALSE并且读入的字节数也返回为0,则表示文件到达了末尾。
<pre>
WriteFile用于文件写,函数原型为:
BOOL WriteFile(
HANDLE hFile, // handle to file
LPCVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToWrite, // number of bytes to write
LPDWORD lpNumberOfBytesWritten, // number of bytes written
LPOVERLAPPED lpOverlapped // overlapped buffer
);
</pre>
参数的含义和ReadFile类似。
<p>如果需要移动文件指针到相关位置(和文件读写不同,这个函数没有异步版本),使用
<pre>
DWORD SetFilePointer(
HANDLE hFile, // handle to file
LONG lDistanceToMove, // bytes to move pointer
PLONG lpDistanceToMoveHigh, // bytes to move pointer
DWORD dwMoveMethod // starting point
);
</pre>
其中各项参数的含义为:
<ul><li>hFile:文件句柄,为CreateFile时返回的句柄
<li>lpBuffer:保存读入的数据的指针
<li>lDistanceToMove:移动的字节数低DWORD
<li>lpDistanceToMoveHigh:移动的字节数高DWORD,为了支持64位(2的64次方字节)长度的大文件,而用来指定64字节的高32位,如果文件大小只需要32位就可以表示,则设置为NULL
<li>ldwMoveMethod:移动方法,可以选择下面的值。<br>FILE_BEGIN 从文件开始处开始移动<br>FILE_CURRENT 从文件开始除开始移动<br>FILE_END 从文件末尾开始移动
</ul>
函数返回值和参数lpDistanceToMoveHigh(当该参数不为NULL时)表明文件指针当前的位置(从文件头到当前的字节数),但当参数lpDistanceToMoveHigh为NULL时如果返回INVALID_SET_FILE_POINTER表明执行失败,当参数lpDistanceToMoveHigh不为NULL时如果返回INVALID_SET_FILE_POINTER还需要判断GetLastError的返回值是否不为NO_ERROR。下面是两种情况下判断错误。
<pre>
//第一种情况
DWORD dwPtr = SetFilePointer (hFile, lDistance, NULL, FILE_BEGIN) ;
if (dwPtr == INVALID_SET_FILE_POINTER) // Test for failure
{
// Obtain the error code.
dwError = GetLastError() ;
// 处理错误
// . . .
} // End of error handler
//第二种情况
dwPtrLow = SetFilePointer (hFile, lDistLow, & lDistHigh, FILE_BEGIN) ;
// Test for failure
if (dwPtrLow == INVALID_SET_FILE_POINTER && (dwError = GetLastError()) != NO_ERROR )
{
// 处理错误
// . . .
} // End of error handler
</pre>
在Win32中没有提供得到文件当前指针位置的函数,但通过SetFilePointer也可以确定文件指针当前的位置。在MSDN中提供了两个宏来得到当前文件的指针位置:
<pre>
//对32位长度的文件
#define GetFilePointer(hFile) SetFilePointer(hFile, 0, NULL, FILE_CURRENT)
//对超过32位长度的文件
#define GetVLFilePointer(hFile, lpPositionHigh) \
(*lpPositionHigh = 0, \
SetFilePointer(hFile, 0, lpPositionHigh, FILE_CURRENT))
</pre>
对了可以通过SetFilePointer来得到文件长度,方法就是从文件位结束处移动0字节,返回值就是文件的长度。
<PRE>
HANDLE hFile = CreateFile(...);//打开文件进行读
DWORD dwLen;
dwLen = SetFilePointer (hFile, 0, NULL, FILE_END) ;
CloseHandle( hFile ) ;
</pre>
当然Win32中也提供了专门的函数来得到文件的大小
<pre>
BOOL GetFileSizeEx(
HANDLE hFile, // handle to file
PLARGE_INTEGER lpFileSize // file size
);
typedef union _LARGE_INTEGER {
struct {
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;
</pre>
其中lpFileSize是一个联合数据,用来表明文件的大小。
<p><a name=overlap></a>文件的异步读写主要是用在大文件的读写上,当使用异步读写时,ReadFile和WriteFile会马上返回,并在读写完成时通知应用程序。
<p>要使用异步功能首先需要在打开文件时指定FILE_FLAG_OVERLAPPED作为标记(dwFlagsAndAttributes),在读写文件时可以使用ReadFile/WriteFile或者ReadFileEx/WriteFileEx来进行读写,当调用不同的函数时读写完成后通知应用程序的方法有所不同的,。下面分别介绍这两种方法:
<p>第一种方法,利用ReadFile/WriteFile,这对函数使用事件信号来进行读写完成的通知,由于磁盘读写是一个比较耗费时间的操作,而且现在的磁盘子系统可以在磁盘读写时脱离CPU而单独进行,例如使用DMA方式,所以在磁盘读写时可以进行其他一些操作以充分利用CPU。关于事件信号相关内容请参考<a href=teach_sp_44.htm#event>4.4节 进程/线程间同步中事件部分内容</a>。并且在文件读写时提供OVERLAPPED数据。
<pre>
结构定义如下:
typedef struct _OVERLAPPED {
ULONG_PTR Internal; //系统使用
ULONG_PTR InternalHigh; //系统使用
DWORD Offset; // 文件读或写的开始位置低32位,对于命名管道和其他通信设备必须设置为0
DWORD OffsetHigh; // 文件读或写的开始位置高32位,对于命名管道和其他通信设备必须设置为0
HANDLE hEvent; // 事件量,当操作完成时,这个时间会变为有信号状态
} OVERLAPPED;
//下面的代码演示了文件异步读取
//并且比较了同步和异步之间的性能差异
void DoDataDeal(BYTE *pbData,int iLen)
{//对字节进行操作
Sleep(3*1000);//假设每次计算需要2秒钟
}
//下面是使用异步读的示范代码,假设c:\temp\large_file.dat文件有130MB大小()
//每次读入10MB字节,并且对文件中每个字节进行操作,由于可以使用异步操作所以可以在下一次读入数据的同时进行计算
void ReadM1(void)
{
HANDLE hFile = CreateFile("c:\\temp\\large_file.dat",GENERIC_READ,0,
NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL,NULL);
if( INVALID_HANDLE_VALUE != hFile )
{
HANDLE hEvent = CreateEvent(NULL,FALSE,FALSE,"read_event");
BYTE *pbRead = new BYTE[1024*1024*10];//10MB字节
BYTE *pbBuf = new BYTE[1024*1024*10];
DWORD dwRead,dwCount=0;
OVERLAPPED overlap;
overlap.Offset = 0;
overlap.OffsetHigh =0;
overlap.hEvent = hEvent;
DWORD dwBegin= GetTickCount();//记录开始时间
ReadFile(hFile,pbRead,1024*1024*10,&dwRead,&overlap);
{//开始计算
for(int i=1;i<13;i++)
{
printf("M1 i=%d\n",i);
WaitForSingleObject(hEvent,INFINITE);//等待上一次完成
memcpy(pbBuf,pbRead,1024*1024*10);
overlap.Offset = i * (1024*1024*10);
overlap.OffsetHigh =0;
overlap.hEvent = hEvent;
ReadFile(hFile,pbRead,1024*1024*10,&dwRead,&overlap);
//在文件进行读的同时进行计算
DoDataDeal(pbBuf,1024*1024*10);
}
WaitForSingleObject(hEvent,INFINITE);//等待最后一次完成
memcpy(pbBuf,pbRead,1024*1024*10);
//数据处理
DoDataDeal(pbBuf,1024*1024*10);
}//结束计算
printf("耗时 %d\n",GetTickCount()-dwBegin);
//操作完成
CloseHandle(hEvent);
CloseHandle(hFile);
delete pbRead;
delete pbBuf;
}
}
//下面是上面功能的文件同步读版本
void ReadM2(void)
{
HANDLE hFile = CreateFile("c:\\temp\\large_file.dat",GENERIC_READ,0,
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if( INVALID_HANDLE_VALUE != hFile )
{
DWORD dwRead,dwCount=0;
BYTE *pbRead = new BYTE[1024*1024*10];//10MB字节
BYTE *pbBuf = new BYTE[1024*1024*10];//10MB字节
DWORD dwBegin= GetTickCount();//记录开始时间
for(int i=0;i<13;i++)
{
printf("M2 i=%d\n",i);
if(!ReadFile(hFile,pbRead,1024*1024*10,&dwRead,NULL))
{
printf("error read");
break;
}
memcpy(pbBuf,pbRead,1024*1024*10);
//计算
DoDataDeal(pbBuf,1024*1024*10);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -