C ++ windows 文件编程API
windows读写文件方法
首先我们需要使用CreateFile
创建文件句柄
使用WriteFile
向文件中写入内容
使用ReadFile
读取文件中的内容
写文件示例WriteFile
#include <Windows.h>
#include <iostream>
using namespace std;
int main1()
{
cout << "Hello world!" << endl;
// 打开文件句柄 会在项目目录下创建1.txt文件
HANDLE hFile = CreateFile(L"1.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); /
// 文件位置,文件模式,0,NULL安全属性,文件存在打开不存在创建,文件写入标志位,NULL模板
// GENERIC_READ:读权限 GENERIC_WRITE:写权限 OPEN_ALWAYS:文件存在则打开,不存在则创建
// 判断是否打开 , hFile文件打开失败会返回INVALID_HANDLE_VALUE
if (INVALID_HANDLE_VALUE == hFile)
{
cout << "Error open! " << endl;
return 1;
}
cout << "open Successful." << endl;
// 写内容到文件中
string content = "hi,Hello world!";
DWORD writtenCount = 0;
BOOL ret = WriteFile(hFile, content.c_str(), content.size(), &writtenCount, NULL);
// 文件句柄,写入字符串指针,写入字符串大小,实际写入计数,NULL异步相关
// ret返回写入成功或者失败
if (false == ret)
{
cout << "写入失败!" << endl;
return 1;
}
cout << "write successful." << endl;
// 刷新缓冲区
FlushFileBuffers(hFile);
// 关闭文件
CloseHandle(hFile);
return 0;
}
读文件示例ReadFile
HANDLE hFile = CreateFile(L"1.txt", GENERIC_READ, NULL, 0, OPEN_EXISTING, NULL, NULL);
// OPEN_EXISTING 文件存在则打开,不存在则报错
if (INVALID_HANDLE_VALUE == hFile)
{
cout << "Error open! " << endl;
return 1;
}
cout << "open Successful." << endl;
string date(255, 0);
DWORD count;
BOOL ret = ReadFile(hFile,&date[0], 255, &count, NULL);
// 255-》需要读取的字节数 count-》实际读取到的字节数
if (false == ret)
{
cerr << "read fail error!" << endl;
}
cout << "size:" << count << "; content" << date << endl;
宽字符集读写文件方法
如果我们将编码设置改为utf编码,而不用ascii编码,则需要修改我们的代码,使其支持宽字符。默认窄字符集每个字符占用1个。
在visual studio中可以在解决方案资源管理器中右键项目名,选择高级,字符集,使用uniocde字符集。
1.首先需要设定字符集
#include <locale> // 先导入locale库
setlocale(LC_ALL, "chs"); //再设置地区为中国
2.向文件中写入宽字符数据
在文件中写入宽字符时,文件头必须先写入0xfeff
,再写入其他内容。
其次写入字符串的大小应该重新计算content.size() * sizeof(wchar_t)
wstring content = L"你好,世界!";
DWORD writtenCount = 0;
wchar_t head = 0xfeff;
BOOL ret = WriteFile(hFile, &head, sizeof(wchar_t),NULL, NULL);
if (false == ret)
{
cerr << "写入失败!" << endl;
return 1;
}
BOOL ret2 = WriteFile(hFile, content.c_str(), content.size() * sizeof(wchar_t), &writtenCount, NULL);
// 文件句柄,写入字符串指针,写入字符串大小,实际写入计数,NULL异步相关
// ret返回写入成功或者失败
if (false == ret2)
{
cerr << "写入失败!" << endl;
return 1;
}
cout << "write successful." << endl;
3.读取文件中宽字符数据
我们读取宽字符文件时,也必须要跳过文件头前两个标识,读取后面的内容。
使用SetFilePointerEx
可以移动文件指针。
DWORD count;
LARGE_INTEGER liMove;
liMove.QuadPart = 2;
SetFilePointerEx(hFile, liMove, NULL, FILE_BEGIN); // 文件指针位移开始的位置-》FILE_BEGIN从头开始 FILE_CURRENT指针当前位置开始 FILE_END文件结尾的位置开始
BOOL ret = ReadFile(hFile, &data[0], 255, &count, NULL);
if (false == ret)
{
cerr << "read fail error!" << endl;
}
wcout << L"size:" << count << L"; content: " << data << endl;
获取文件大小GetFileSize
HANDLE hfile = CreateFile(L"1.txt", 0, 0, NULL, OPEN_EXISTING, NULL, NULL);
// 第二个参数,给0代表既不给读权限也不给写权限
if (INVALID_HANDLE_VALUE == hfile)
{
cout << "Error open! " << endl;
return 1;
}
ULARGE_INTEGER ulFileSize; // 只能传入ultra large interger数据
//方法一: GetFileSize 返回低位数据,第二个参数传入高位数据的地址
ulFileSize.LowPart = GetFileSize(hfile, &ulFileSize.HighPart);
cout << "file size is " << ulFileSize.QuadPart << endl;
// 方法二: GetFileSizeEx 返回是否获取成功的布尔值,传入large integer类型
LARGE_INTEGER lFileSize;
BOOL ret = GetFileSizeEx(hfile, &lFileSize);
if (ret)
{
cout << "file size is " << lFileSize.QuadPart << endl;
}
CloseHandle(hfile);
设置文件结束位置SetFilePointerEx
LARGE_INTEGER liMove;
liMove.QuadPart = 2;
SetFilePointerEx(hfile, liMove, NULL, FILE_BEGIN);
SetEndOfFile(hfile); // 设置文件尾的位置,可以影响文件的大小
异步读写
同步读写,当读写大文件时,会产生堵塞,直到读写完成才进行后续操作;异步读写会在另一个线程进行读写操作,无需flushfilebuffer。同时,读写函数也不会立即返回读写函数执行的结果。
读写flag位改为FILE_FLAG_OVERLAPPED
判断读写成功的方法 GetLastError()获取的值于ERROR_IO_PENDING对比,如果相同则在进行异步操作。
异步写
OVERLAPPED ol1 = { 0 }; // 异步读写必须要的参数
BOOL ret = WriteFile(hFile, content.c_str(), content.size(), &writtenCount, &ol1);
// 文件句柄,写入字符串指针,写入字符串大小,实际写入计数,NULL异步相关
// 异步读写,ret一定是fail
if (false == ret)
{
if (ERROR_IO_PENDING == GetLastError())
{
cout << "正在进行异步操作!" << endl;
}
else
{
cout << "写入失败!" << endl;
return 1;
}
}
cout << "write successful." << endl;
// 刷新缓冲区 异步操作不需要刷新缓冲区
//FlushFileBuffers(hFile);
异步读
OVERLAPPED ol = { 0 };
BOOL ret = ReadFile(hFile, &date[0], 255, &count, &ol);
// 异步读写返回值一定会fail
if (false == ret)
{
if (ERROR_IO_PENDING == GetLastError())
{
cout << "正在进行异步操作!" << endl;
}
else
{
getchar();
cerr << "read fail error!" << endl;
}
}
方式一:触发设备内核对象
WaitForSingleObject:等待线程执行结束后再向下执行;如果该句柄开多个异步线程,等待最快的一个线程执行结束后向下执行。
读操作
string date(255, 0);
DWORD count;
OVERLAPPED ol = { 0 };
BOOL ret = ReadFile(hFile, &date[0], 255, &count, &ol);
// 异步读写返回值一定会fail
if (false == ret)
{
if (ERROR_IO_PENDING == GetLastError())
{
cout << "正在进行异步操作!" << endl;
// 等待hfile线程执行结束后,再向下读取文件
WaitForSingleObject(hFile, INFINITE);
cout << "size:" << count << "; content " << date << endl;
}
else
{
cerr << "read fail error!" << endl;
}
}
写操作
string content = "hi,Hello world! 2024";
DWORD writtenCount = 0;
OVERLAPPED ol1 = { 0 }; // 异步读写必须要的参数
BOOL ret = WriteFile(hFile, content.c_str(), content.size(), &writtenCount, &ol1);
// 文件句柄,写入字符串指针,写入字符串大小,实际写入计数,NULL异步相关
// 异步读写,ret一定是fail
if (false == ret)
{
if (ERROR_IO_PENDING == GetLastError())
{
cout << "正在进行异步操作!" << endl;
WaitForSingleObject(hFile, INFINITE);
cout << "异步写入完成" << endl;
}
else
{
cout << "写入失败!" << endl;
return 1;
}
}
方式二:触发事件内核对象
int main()
{
cout << "Hello world!" << endl;
HANDLE hFile = CreateFile(L"3.txt", GENERIC_READ, NULL, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
cout << "Error open! " << endl;
return 1;
}
cout << "open Successful." << endl;
string date1(255, 0);
string date2(255, 0);
DWORD count;
OVERLAPPED ol = { 0 };
OVERLAPPED ol2 = { 0 };
ol.hEvent = CreateEvent(NULL, true, false, NULL);
ol2.hEvent = CreateEvent(NULL, true, false, NULL);
BOOL ret1 = ReadFile(hFile, &date1[0], 255, NULL, &ol);
BOOL ret2 = ReadFile(hFile, &date2[0], 255, NULL, &ol2);
// 异步读写返回值一定会fail
if (false == ret1 && false == ret2)
{
if (ERROR_IO_PENDING == GetLastError())
{
cout << "正在进行异步操作!" << endl;
// 等待hfile线程执行结束后,再向下读取文件
HANDLE h[2];
h[0] = ol.hEvent;
h[1] = ol2.hEvent;
DWORD retNum = WaitForMultipleObjects(2, h, true, INFINITE);
switch (retNum)
{
case WAIT_OBJECT_0:
cout << "handle 1 complete;" << endl;
break;
case WAIT_OBJECT_0 + 1:
cout << "handle 2 complete;" << endl;
break;
default:
break;
}
cout << "1 content " << date1 << endl;
cout << "2 content " << date2 << endl;
}
else
{
cerr << "read fail error!" << endl;
}
}
CloseHandle(ol.hEvent);
CloseHandle(ol2.hEvent);
CloseHandle(hFile);
return 0;
}
方式三:使用可提醒IO
string content = "hi,Hello world! 2024/1/15";
VOID WriteFunc(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
) {
cout << "写入成功!" << endl;
}
int main42()
{
cout << "Hello world!" << endl;
// 打开文件句柄
HANDLE hFile = CreateFile(L"3.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
// 文件位置,文件模式,0,NULL安全属性,文件存在打开不存在创建,FILE_FLAG_OVERLAPPED代表异步读写操作,NULL模板
//
// 判断是否打开
if (INVALID_HANDLE_VALUE == hFile)
{
cout << "Error open! " << endl;
return 1;
}
cout << "open Successful." << endl;
// 写内容到文件中
DWORD writtenCount = 0;
OVERLAPPED ol1 = { 0 }; // 异步读写必须要的参数
BOOL ret = WriteFileEx(hFile, content.c_str(), content.size(),&ol1,(LPOVERLAPPED_COMPLETION_ROUTINE)WriteFunc);
// 文件句柄,写入字符串指针,写入字符串大小,实际写入计数,NULL异步相关
// 异步读写,ret一定是fail
SleepEx(1000, true);// sleep 1000ms ,第二个参数alert必须为true
if (false == ret)
{
if (ERROR_IO_PENDING == GetLastError())
{
}
else
{
cout << "写入失败!" << endl;
return 1;
}
}
// 刷新缓冲区 异步操作不需要刷新缓冲区
//FlushFileBuffers(hFile);
// 关闭文件
CloseHandle(hFile);
return 0;
}
使用IO完成端口
串行模型:排队一个一个处理
并发模型:多个线程,来一个创建一个线程;线程开销大,而且cpu在不同线程间切换,可能还没处理完这个任务就切换到下一个任务
IO完成端口是:多线程,当一个线程完成之后再切换新的线程去处理请求
使用CreateIoCompletionPort去创建并绑定一个句柄;只有句柄里有消息传入GetQueuedCompletionStatus就会返回TRUE,如果想退出再开一个线程,在另一个线程里设置条件触发PostQueuedCompletionStatus用来结束监听。
#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;
HANDLE hCicp;
unsigned int threadFunc(void* args)
{
getchar();
ULONG_PTR key = 10;
OVERLAPPED ol = { 0 };
// 用来结束监听,key是结束时返回标志,会从传递给GetQueuedCompletionStatus的lpContext
PostQueuedCompletionStatus(hCicp, 4, key, &ol);
return 0;
}
int main()
{
HANDLE hFile = CreateFile(L"1.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
cerr << "open file handle fail" << endl;
return -1;
}
// 创建 Io Completion Port 并绑定hFile
hCicp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (NULL == hCicp)
{
CloseHandle(hFile);
cerr << "create Io Completion Port handle fail" << endl;
return -1;
}
ULONG_PTR CK_READ = 0;
CreateIoCompletionPort(hFile, hCicp, CK_READ, 0);
// read file
string enContent_r(255, 0);
OVERLAPPED ol = { 0 };
// 异步读取中,ret一定是FALSE,不用去判断
BOOL ret = ReadFile(hFile, &enContent_r[0], 255, NULL, &ol);
unsigned int threadID = 0;
// 创建一个新线程用来控制IO完成端口结束
_beginthreadex(NULL, 0, (_beginthreadex_proc_type)threadFunc, NULL, 0, &threadID);
DWORD transBytes = 0; // 读到的字节数
void* lpContext = NULL; // io设备完成io之后传过来的附加参数,PostQueuedCompletionStatus传过来的
OVERLAPPED* pOl = NULL; // 异步相关的参数
while (GetQueuedCompletionStatus(hCicp, &transBytes, (PULONG_PTR)&lpContext, &pOl, INFINITE))
{
if (NULL != lpContext && 10 == (unsigned int)lpContext)
{
cout << "我要退出了!" << endl;
break;
}
cout << enContent_r << endl;
}
CloseHandle(hFile);
CloseHandle(hCicp);
return 0;
}