windowsViaC/C++之设备I/O之文件设备

来源:互联网 发布:gta5ol女角色捏脸数据 编辑:程序博客网 时间:2024/05/23 10:50

本文转载于http://www.cnblogs.com/wz19860913/archive/2008/08/17/1269729.html,谢谢原作者的辛苦付出。

本来不打算写这篇的,但是文件的重要性大家都知道。在设备I/O中,有一种设备叫文件设备,这是一个抽象的概念,就把它理解为文件就行了。文件设备,可以通过CreateFile函数打开,得到一个文件对象句柄。 

在文件中,有两个比较重要的属性:
1、文件大小:在32位中最大为4GB,64位中可以达到16EB。
2、文件读写指针:这个指针表明读写位置,大小范围可以超出文件的大小。
先讨论文件的大小。
要得到文件的大小,可以使用GetFileSizeEx函数:

BOOL GetFileSizeEx(
   HANDLE         hFile,     //文件对象句柄
   PLARGE_INTEGER pliFileSize);  //LARGE_INTEGER联合的指针,返回大小
 这个函数接受一个LARGE_INTEGER联合的指针,用来返回文件大小,这个结构可以表示64位值:
typedef union _LARGE_INTEGER {
   struct {
      DWORD LowPart;    // 低32位值
      LONG HighPart;       // 高32位值


   };
   LONGLONG QuadPart;   // 64位值得
} LARGE_INTEGER, *PLARGE_INTEGER;
从这个定义可以看出,该联合可以用QuadPart表示一个64位值,也可以差分成两个32位值。这个联合有一个无符号数版本,叫做ULAGER_INTEGER联合,对应的3个成员都是保存的无符号数。
还有一个函数可以得到一个文件的大小:


DWORD GetCompressedFileSize(
   PCTSTR pszFileName,          //文件路径名
   PDWORD pdwFileSizeHigh); //文件大小如果大于4GB,高32位值由该参数返回
 这个函数接受一个文件的路径名称,返回文件大小的低32位值,高32位值由参数pdwFileSizeHigh返回。
 与GetFileSizeEx不同的是,该函数返回一个文件的物理大小,而GetFileSizeEx返回文件的逻辑大小。
比如一个文件大小为100KB,它被压缩为85KB,如果使用GetFileSizeEx,则返回100KB,使用GetCompressedFileSize则返回85KB。
与GetFileSizeEx不同的是,该函数接受一个字符串,指明文件路径,这就可以直接查询某个文件大小,而不要先打开它获得它的句柄。
可以如下使用该函数:
ULARGE_INTEGER ulFileSize;     //与LARGE_INTEGER联合类似,保存无符号数
ulFileSize.LowPart = GetCompressedFileSize(TEXT("SomeFile.dat"),
   &ulFileSize.HighPart);     //取得当前目录下的SomeFile.dat文件大小
这样,64位的文件大小存储在ulFileSize.QuadPart中。

讨论完了文件的大小,下面来讨论文件读写指针。
CreateFile函数创建或打开了一个文件内核对象,该内核对象中管理着一个“文件读写指针”。
该指针指明了一个64位的偏移量。初始情况下,该指针设置为0,即你读取或写入数据的时候从文件开始处进行,即从偏移量为0的地方开始。
每次读取或写入N字节的数据,系统更新该读写指针,使偏移量加上N个字节。比如下面代码反映了读取文件前100个字节的数据:
BYTE pbFirst[50], pbSecond[50];
DWORD dwNumBytes;
HANDLE hFile = CreateFile(TEXT("MyFile.dat"), ...); //指针初始化为0
ReadFile(hFile, pbFirst, 50, &dwNumBytes, NULL);    //读取第0~49字节
ReadFile(hFile, pbSecond, 50, &dwNumBytes, NULL);//读取第50~99字节


需要注意的是,一个文件对象句柄对应一个读写指针,如果一个文件被打开多次,那么就有多个文件对象,每个文件对象管理着一个读写指针,这些指针相互之间不影响。比如下面的代码:
BYTE pb[10];
DWORD dwNumBytes;
HANDLE hFile1 = CreateFile(TEXT("MyFile.dat"), ...); //指针初始化为0
HANDLE hFile2 = CreateFile(TEXT("MyFile.dat"), ...); //指针初始化为0
ReadFile(hFile1, pb, 10, &dwNumBytes, NULL);  //读取第0~9字节
ReadFile(hFile2, pb, 10, &dwNumBytes, NULL);  //也是读取第0~9字节
上面这段代码,hFile1和hFile2是同一个文件的两个不同的文件内核对象的句柄,这两个内核对象管理着两个不同文件指针,所以改变其中一个的读写指针,不会影响另一个。

BYTE pb[10];
DWORD dwNumBytes;
HANDLE hFile1 = CreateFile(TEXT("MyFile.dat"), ...); //读写指针初始化为0
HANDLE hFile2;     //另一个文件句柄


//将本进程内hFile1句柄值复制给本进程中的hFile2
DuplicateHandle(
   GetCurrentProcess(), hFile1,
   GetCurrentProcess(), &hFile2,
   0, FALSE, DUPLICATE_SAME_ACCESS);
ReadFile(hFile1, pb, 10, &dwNumBytes, NULL);   //读取第0~9字节
ReadFile(hFile2, pb, 10, &dwNumBytes, NULL);   //读取第10~19字节

上面这段代码,使用DuplicateHandle函数复制句柄,使得两个句柄hFile1和hFile2共用同一个文件内核对象,因此读写指针也是共用的。

 可以使用SetFilePointerEx函数来定位文件读写指针:
BOOL SetFilePointerEx(
   HANDLE         hFile,     //文件内核对象句柄
   LARGE_INTEGER  liDistanceToMove,     //64位数,移动字节数
   PLARGE_INTEGER pliNewFilePointer,    //返回新的文件读写指针位置
   DWORD          dwMoveMethod);        //移动方式
该函数中dwMoveMethod告诉系统如何移动。FILE_BEGIN,表示从文件头开始移动;FILE_END,表示从文件尾往前移动;FILE_CURRENT,表示从当前读写指针位置移动。移动的位移量在第2个参数liDistaceToMove中。

 有几点需要注意
将文件读写指针的位置设置为超过文件大小范围是合法的。这么做不会使得文件大小变大,除非调用函数SetEndOfFile。
当打开文件使用函数CreateFile时,该函数的dwFlagsAndAttributes参数中包括FILE_FLAG_NO_BUFFERING,文件读写指针只能被设置为硬盘扇区的单位大小。
没有GetFilePointerEx函数来取得当前文件指针位置,可以调用SetFilePointerEx函数来得到其位置,要把第二个参数设置为0,如下代码:

LARGE_INTEGER liCurrentPosition = { 0 };
SetFilePointerEx(hFile, liCurrentPosition,
                        &liCurrentPosition,FILE_CURRENT);
 
当文件被关闭的时候,系统会在文件上设置一个结束位置,以确定该文件的大小。当然,你也可以自己设置文件的结束位置,以此来改变文件的大小。使用SetEndOfFile函数:
BOOL SetEndOfFile(HANDLE hFile);
 该文件在当前的文件读写指针处设置文件的结束标志,来截断或扩展文件的大小。比如,你想设置一个文件的大小为1024字节的话,可以通过以下代码实现:
HANDLE hFile = CreateFile(...);
LARGE_INTEGER liDistanceToMove;
liDistanceToMove.QuadPart = 1024;
//设置文件指针
SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN);
SetEndOfFile(hFile);     //在文件指针处设置结束标志
CloseHandle(hFile);

0 0
原创粉丝点击