API读写32位系统4G以上大文件
32位操作系统存放4G以上大文件需要NTFS分区支持,FAT32分区不支持且没优势。支持文件操作的有dos操作方式的、API操作方式的、编译器封装后的。dos文件操作方式有fopen,fclose,fwrite,fread,ftell,fseek,int型变量大多数编译器默认为32位字长,符号占一位,可寻址31位,寻址范围为 2^31 = 2.147GB。API操作方式说明如下:
--------------------------------------------------------------------------------
创建文件:
HANDLE WINAPI CreateFile( LPCTSTR lpFileName, // 文件名字符长指针。 DWORD dwDesiredAccess, // 文件的操作访问方式 DWORD dwShareMode, // 共享模式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性 DWORD dwCreationDisposition, // 创建方式 DWORD dwFlagsAndAttributes, // 文件属性 HANDLE hTemplateFile // 模板文件 );
返回:HANDLE,文件句柄,定义如下:
typedef PVOID HANDLE; // 类型定义为空指针。 typedef void* PVOID; // 空指针类型定义。
成功返回正数句柄值,失败返回INVALID_HANDLE_VALUE,定义如下:
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) // 为 -1 数值
参数(1) lpFileName:文件名字符长指针。
参数(2) dwDesiredAccess, 文件的访问请求,无符长字变量(DWORD),主要定义如下:
#define GENERIC_READ (0x80000000L) // 读文件请求 #define GENERIC_WRITE (0x40000000L) // 写文件请求
因为是掩码,可以通过 |(或)组合,比如:GENERIC_READ | GENERIC_WRITE 读或写。
参数(3) dwShareMode:共享模式,无符长字变量(DWORD),主要定义如下:
#define FILE_SHARE_READ 0x00000001 // 打开文件,只有在文件的访问请求(2),为读时才能返回成功。 #define FILE_SHARE_WRITE 0x00000002 // 打开文件,只有在文件的访问请求(2),为写时才能返回成功。
如果CreateFile打开成功,则其他程序不能访问,只有在使用CloseHandle关闭句柄后,才可使用。
参数(4) lpSecurityAttributes:安全属性结构指针,引用LPSECURITY_ATTRIBUTES结构,确定句柄是否被子进程继承。如果为NULL,则不能被子进程继承。
参数(5) dwCreationDisposition:创建方式,无符长字变量(DWORD),主要定义如下:
#define CREATE_NEW 1 // 创建新文件,如果文件存在就返回失败。 #define CREATE_ALWAYS 2 // 总是创建,如果文件存在就覆盖它。 #define OPEN_EXISTING 3 // 打开文件,如果文件不存在就返回失败。 #define OPEN_ALWAYS 4 // 打开已存在文件,如果文件不存在,在使用CREATE_NEW(或)组合时,则创建一个新文件。
参数(6) dwFlagsAndAttributes:文件属性,无符长字变量(DWORD),主要定义如下:
#define FILE_ATTRIBUTE_READONLY 0x00000001 // 只读文件属性 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 // 隐藏属性 #define FILE_ATTRIBUTE_NORMAL 0x00000080 // 通常的文件属性,必须单独使用。
参数(7) hTemplateFile:模板文件,文件句柄类型。
可能存在的应用如下:
1. HANDLE hFile = CreateFile(sFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
(1) sFileName:文件路径及文件名。
(2) GENERIC_WRITE:写文件请求。
(3) FILE_SHARE_WRITE:独立写。
(4) NULL:不被子进程继承。
(5) CREATE_ALWAYS:总是创建,如果文件存在就覆盖它。
(6) FILE_ATTRIBUTE_NORMAL:通常的文件属性
(7) NULL:不使用文件模板。
说明:打开文件准备写,如果文件存在,正常打开,并清空数据,如果文件不存在,则创建。
//测试
void __fastcall TForm1::SpeedButton1Click(TObject *Sender) { HANDLE hFile; String sFileName;
sFileName = "D:\\1.txt"; hFile = CreateFile(sFileName.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { MessageBox(HWND_DESKTOP, "打开写或创建文件失败,系统发生错误。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } }
2. HANDLE hFile = CreateFile(sFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
(1) sFileName:文件路径及文件名。
(2) GENERIC_READ:读文件请求。
(3) FILE_SHARE_READ:独立读。
(4) NULL:不被子进程继承。
(5) OPEN_EXISTING:打开文件,如果文件不存在就返回失败。
(6) FILE_ATTRIBUTE_NORMAL:通常的文件属性
(7) NULL:不使用文件模板。
说明:打开文件准备读,如果文件存在,则正常打开,如果文件不存在,则返回失败。
//测试
void __fastcall TForm1::SpeedButton2Click(TObject *Sender) { HANDLE hFile; String sFileName; sFileName = "D:\\18.txt"; hFile = CreateFile(sFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { MessageBox(HWND_DESKTOP, "打开读文件失败,因为文件不存在。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } }
--------------------------------------------------------------------------------
设置文件指针:
BOOL WINAPI SetFilePointerEx( HANDLE hFile, // 已打开的文件句柄 LARGE_INTEGER liDistanceToMove, // 移动位置 PLARGE_INTEGER lpNewFilePointer,// 返回新移动到的位置 DWORD dwMoveMethod // 移动方法 );
返回:BOOL,如果失败,返回False,如果成功返回True。
参数(1) hFile:由CreateFile打开的文件句柄。
参数(2) liDistanceToMove:移动位置,引用LARGE_INTEGER结构,传递64位文件位置变量。通过LARGE_INTEGER结构64位变量接口输入。
__int64 iFilePointerPos; //64位文件指针位置变量
liDistanceToMove.QuadPart = iFilePointerPos; //传递64位变量
(liDistanceToMove.LowPart为DWORD类型,liDistanceToMove.HighPart为LONG类型,合成为有符合64位类型变量)
LARGE_INTEGER结构定义如下:
#if defined(MIDL_PASS) typedef struct _LARGE_INTEGER { #else // MIDL_PASS typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; }; struct { DWORD LowPart; LONG HighPart; } u; #endif //MIDL_PASS LONGLONG QuadPart; } LARGE_INTEGER;
参数(3) lpNewFilePointer:返回新移动到的位置指针,引用LARGE_INTEGER结构,如果不使用,可以为NULL。
参数(4) dwMoveMethod:移动方法,无符长字变量(DWORD),主要定义如下:
#define FILE_BEGIN 0 //从文件开始处移动 #define FILE_CURRENT 1 //从文件当前处移动 #define FILE_END 2 //从文件尾部处移动
可能存在的应用:通过64位有符号变量范围内的文件位置,实现对大文件的读写。
//测试
void __fastcall TForm1::SpeedButton4Click(TObject *Sender) { HANDLE hFile; String sFileName; LARGE_INTEGER liDistanceToMove; __int64 iFilePointerPos; sFileName = "D:\\1.txt"; hFile = CreateFile(sFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { MessageBox(HWND_DESKTOP, "打开读文件失败,因为文件不存在。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } iFilePointerPos = 10; //设置文件位置 liDistanceToMove.QuadPart = iFilePointerPos; //传递64位变量 SetFilePointerEx(hFile, liDistanceToMove, NULL, FILE_BEGIN); }
--------------------------------------------------------------------------------
读文件:
BOOL WINAPI ReadFile( HANDLE hFile, // 已打开的文件句柄 LPVOID lpBuffer, // 读出的缓冲区 DWORD nNumberOfBytesToRead,// 读出的字节量 LPDWORD lpNumberOfBytesRead, // 反馈实际读出的字节量 LPOVERLAPPED lpOverlapped // 异步读写的结构指针 );
返回:BOOL,如果失败,返回False,如果成功返回True。
参数(1) hFile:由CreateFile打开的文件句柄。
参数(2) lpBuffer:空指针,由LPCVOID类型定义,读出时确定变量类型。
参数(3) nNumberOfBytesToWrite:读出的字节量,无符号长字类型,每次可读出 2^32位 = 4.294GB 字节量。
参数(4) lpNumberOfBytesWritten:反馈实际读出的字节量,无符号长字变量指针。
参数(5) lpOverlapped:异步读写的结构指针,引用LPOVERLAPPED结构,如果不使用,可以为NULL。
//测试
void __fastcall TForm1::SpeedButton6Click(TObject *Sender) { HANDLE hFile; String sFileName; bool bRet; BYTE chBuf[4]; DWORD dwReadCount,dwRetReadCount; String sS; sFileName = "D:\\1.txt"; hFile = CreateFile(sFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { MessageBox(HWND_DESKTOP, "打开读文件失败,因为文件不存在。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } dwReadCount = 4; bRet = ReadFile(hFile, chBuf, dwReadCount, &dwRetReadCount, NULL);//读文件 if(!bRet) { MessageBox(HWND_DESKTOP, "读文件失败。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } sS.SetLength(4);//设置字符长度 for(int i = 0; i < 4; i++) sS[i+1] = chBuf[i];//拷贝字符 StaticText1->Caption = sS;//显示字符 }
--------------------------------------------------------------------------------
写文件
BOOL WINAPI WriteFile( HANDLE hFile, // 已打开的文件句柄 LPCVOID lpBuffer, // 写入的缓冲区 DWORD nNumberOfBytesToWrite, // 写入的字节量 LPDWORD lpNumberOfBytesWritten,// 反馈实际写入的字节量 LPOVERLAPPED lpOverlapped // 异步读写的结构指针 );
返回:BOOL,如果失败,返回False,如果成功返回True。
参数(1) hFile:由CreateFile打开的文件句柄。
参数(2) lpBuffer:空指针,由LPCVOID类型定义,写入时确定变量类型。
参数(3) nNumberOfBytesToWrite:写入的字节量,无符号长字类型,每次可写入 2^32位 = 4.294GB 字节量。
参数(4) lpNumberOfBytesWritten:反馈实际写入的字节量,无符号长字变量指针。
参数(5) lpOverlapped:异步读写的结构指针,引用LPOVERLAPPED结构,如果不使用,可以为NULL。
//测试
void __fastcall TForm1::SpeedButton5Click(TObject *Sender) { HANDLE hFile; String sFileName; bool bRet; BYTE chBuf[4] = {'T','e','s','t'}; DWORD dwWriteCount,dwRetWriteCount; sFileName = "D:\\1.txt"; hFile = CreateFile(sFileName.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { MessageBox(HWND_DESKTOP, "打开写或创建文件失败,系统发生错误。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } dwWriteCount = 4; bRet = WriteFile(hFile, chBuf, dwWriteCount, &dwRetWriteCount, NULL);//写文件 if(!bRet) { MessageBox(HWND_DESKTOP, "写文件失败,请检查文件所在磁盘分区容量,或系统发生错误。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } }
--------------------------------------------------------------------------------
关闭句柄
BOOL CloseHandle( HANDLE hObject // 句柄对象 );
返回:BOOL,如果失败,返回False,如果成功返回True。
参数(1) hObject:已打开的句柄对象,引用HANDLE的句柄对象。
//测试
void __fastcall TForm1::SpeedButton7Click(TObject *Sender) { HANDLE hFile; String sFileName; bool bRet; sFileName = "D:\\1.txt"; hFile = CreateFile(sFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { MessageBox(HWND_DESKTOP, "打开读文件失败,因为文件不存在。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } bRet = CloseHandle(hFile);//关闭文件 if(!bRet) { MessageBox(HWND_DESKTOP, "关闭文件失败。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } }
--------------------------------------------------------------------------------
获取文件长度
BOOL WINAPI GetFileSizeEx( HANDLE hFile, //文件句柄 PLARGE_INTEGER lpFileSize //返回文件长度 );
返回:BOOL,失败返回False,成功返回True。
参数(1) hFile:由CreateFile打开的文件句柄。
参数(2) lpFileSize:返回文件长度,引用LARGE_INTEGER的结构指针,通过LARGE_INTEGER结构64位变量接口输出,传递64位变量。
typedef LARGE_INTEGER *PLARGE_INTEGER;//PLARGE_INTEGER结构指针类型定义
//测试
void __fastcall TForm1::SpeedButton8Click(TObject *Sender) { String sFileName; HANDLE hFile; PLARGE_INTEGER lpFileSize; __int64 iFileSize; bool bRet; sFileName = "D:\\1.txt"; hFile = CreateFile(sFileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { MessageBox(HWND_DESKTOP, "打开读文件失败,因为文件不存在。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } bRet = GetFileSizeEx(hFile, lpFileSize);//获取文件长度 if(!bRet) { MessageBox(HWND_DESKTOP, "读文件长度失败。", "错误", MB_OK | MB_ICONINFORMATION); return;//返回不再继续。 } iFileSize = lpFileSize->QuadPart;//传递变量 StaticText1->Caption = IntToStr(iFileSize);//显示文件长度 }
--------------------------------------------------------------------------------
总结:ReadFile,WriteFile,一次可读写4.294GB内的数据,SetFilePointerEx移动文件读写位置为63位(一个符号位),可实现大于4.294GB的读写,2^63 = 9223372037GB足够大,不爽的是需要通过移动读写位置,间接实现。
--------------------------------------------------------------------------------
注1:以上在32位XP操作系统,C++Builder 6 测试通过。
注2:将API封装为一个文件类,在获取文件长度时出现了两次错误,内容如下:
错误1:
__int64 __fastcall TApiFile::GetFileSize(void)//获取文件长度 { PLARGE_INTEGER lpFileSize; bool bRet; bRet = GetFileSizeEx(hFile, lpFileSize);//获取文件长度 if(!bRet) return false;//失败返回 return lpFileSize->QuadPart; }
因使用GetFileSizeEx,忽略了API还有个GetFileSize,重名错误,将其更名。
错误2:
__int64 __fastcall TApiFile::GetFileLength(void)//获取文件长度 { PLARGE_INTEGER lpFileSize; bool bRet; bRet = GetFileSizeEx(hFile, lpFileSize);//获取文件长度 if(!bRet) return false;//失败返回 return lpFileSize->QuadPart; }
一调用就崩溃,问题出在PLARGE_INTEGER lpFileSize结构指针引用问题,指针不是变量,造成崩溃,用LARGE_INTEGER FileSize结构引用,因存在具体变量,在输入结构参数时取地址,问题得以解决。
正确程序:
__int64 __fastcall TApiFile::GetFileLength(void)//获取文件长度 { LARGE_INTEGER FileSize; bool bRet; bRet = GetFileSizeEx(hFile, &FileSize);//获取文件长度 if(!bRet) return false;//失败返回 return FileSize.QuadPart; }
经测试没问题。为什么在TForm主程序不崩溃可以正常,一时难以说清楚。遇到函数参数为指针的,一定弄清楚是输入还是输出,再看一下获取文件长度原型:
BOOL WINAPI GetFileSizeEx( _In_ HANDLE hFile, // 输入,文件句柄 _Out_ PLARGE_INTEGER lpFileSize // 输出,文件长度 );
PLARGE_INTEGER lpFileSize是输出的,指向LARGE_INTEGER结构的指针,是函数向外输出的,必须有具体结构变量地址,才能接收数据。如果是输入的指针,必须先指向具体的变量、结构等,才能传入数据。还有一类是_In_opt_的,要弄清楚输入输出关系。