在Win32系统下文件可以支持平常的同步读写和异步读写(但在Win9X下,Win32系统不支持磁盘文件的异步读写)。本节在后面部分将会介绍文件的异步读写,最后一段内容将向大家讲解一下文件的区域加锁。 在Win32系统中支持64位长度的文件,所以在很多文件操作函数中需要两个DWORD参数来表示文件长度,一个DWORD用来表示低32位,另一个用来表示高32位。 文件的读写进行在文件被正确打开后,但请确认在打开文件时设置了正确的读写标记。在Win32的文件操作中没有了以前类似与以前ANSI C中的fputs fgets fprintf fscanf等函数,只有类似于fread和fwrite的ReadFile和WriteFile函数。 ReadFile用于文件读,函数原型为: 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
);
其中各项参数的含义为: - hFile:文件句柄,为CreateFile时返回的句柄
- lpBuffer:保存读入的数据的指针
- nNumberOfBytesToRead:指定需要读入的字节数
- lpNumberOfBytesRead:返回实际读入的字节数
- lpOverlapped:在文件异步读写时使用的数据,在同步读写中全部都设置为NULL,在Win9X中只支持对串口的异步操作。
如果返回值为FALSE并且读入的字节数也返回为0,则表示文件到达了末尾。 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
);
参数的含义和ReadFile类似。 如果需要移动文件指针到相关位置(和文件读写不同,这个函数没有异步版本),使用 DWORD SetFilePointer(
HANDLE hFile, // handle to file
LONG lDistanceToMove, // bytes to move pointer
PLONG lpDistanceToMoveHigh, // bytes to move pointer
DWORD dwMoveMethod // starting point
);
其中各项参数的含义为: - hFile:文件句柄,为CreateFile时返回的句柄
- lpBuffer:保存读入的数据的指针
- lDistanceToMove:移动的字节数低DWORD
- lpDistanceToMoveHigh:移动的字节数高DWORD,为了支持64位(2的64次方字节)长度的大文件,而用来指定64字节的高32位,如果文件大小只需要32位就可以表示,则设置为NULL
- ldwMoveMethod:移动方法,可以选择下面的值。
FILE_BEGIN 从文件开始处开始移动 FILE_CURRENT 从文件开始除开始移动 FILE_END 从文件末尾开始移动 函数返回值和参数lpDistanceToMoveHigh(当该参数不为NULL时)表明文件指针当前的位置(从文件头到当前的字节数),但当参数lpDistanceToMoveHigh为NULL时如果返回INVALID_SET_FILE_POINTER表明执行失败,当参数lpDistanceToMoveHigh不为NULL时如果返回INVALID_SET_FILE_POINTER还需要判断GetLastError的返回值是否不为NO_ERROR。下面是两种情况下判断错误。 //第一种情况
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
在Win32中没有提供得到文件当前指针位置的函数,但通过SetFilePointer也可以确定文件指针当前的位置。在MSDN中提供了两个宏来得到当前文件的指针位置: //对32位长度的文件
#define GetFilePointer(hFile) SetFilePointer(hFile, 0, NULL, FILE_CURRENT)
//对超过32位长度的文件
#define GetVLFilePointer(hFile, lpPositionHigh) \
(*lpPositionHigh = 0, \
SetFilePointer(hFile, 0, lpPositionHigh, FILE_CURRENT))
对了可以通过SetFilePointer来得到文件长度,方法就是从文件位结束处移动0字节,返回值就是文件的长度。 HANDLE hFile = CreateFile(...);//打开文件进行读
DWORD dwLen;
dwLen = SetFilePointer (hFile, 0, NULL, FILE_END) ;
CloseHandle( hFile ) ;
当然Win32中也提供了专门的函数来得到文件的大小 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;
其中lpFileSize是一个联合数据,用来表明文件的大小。 文件的异步读写主要是用在大文件的读写上,当使用异步读写时,ReadFile和WriteFile会马上返回,并在读写完成时通知应用程序。 要使用异步功能首先需要在打开文件时指定FILE_FLAG_OVERLAPPED作为标记(dwFlagsAndAttributes),在读写文件时可以使用ReadFile/WriteFile或者ReadFileEx/WriteFileEx来进行读写,当调用不同的函数时读写完成后通知应用程序的方法有所不同的,。下面分别介绍这两种方法: 第一种方法,利用ReadFile/WriteFile,这对函数使用事件信号来进行读写完成的通知,由于磁盘读写是一个比较耗费时间的操作,而且现在的磁盘子系统可以在磁盘读写时脱离CPU而单独进行,例如使用DMA方式,所以在磁盘读写时可以进行其他一些操作以充分利用CPU。关于事件信号相关内容请参考4.4节 进程/线程间同步中事件部分内容。并且在文件读写时提供OVERLAPPED数据。 结构定义如下:
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);
}
printf("耗时 %d\n",GetTickCount()-dwBegin);
//操作完成
CloseHandle(hFile);
delete pbRead;
delete pbBuf;
}
}
在文件的异步读写中,如果ReadFile/WriteFile返回FALSE,则需要通过LastError来进一步确认是否发生错误,例如下面的错误检测代码: bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead,
&gOverlapped) ;
if (!bResult)
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
//到达文件尾
}
case ERROR_IO_PENDING:
{
//正在进行异步操作
} // end case
} // end switch
} // end if
此外可以通过GetOverlappedResult函数来得到异步函数的执行情况
BOOL GetOverlappedResult(
HANDLE hFile, // handle to file, pipe, or device
LPOVERLAPPED lpOverlapped, // overlapped structure
LPDWORD lpNumberOfBytesTransferred, // bytes transferred
BOOL bWait // wait option
);
|