C ++ windows 文件编程API

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;
}
点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注