Windows Practice_文件_内存映射(一)

大文件数据操作

我们以全国开放数据(数据纯属伪造)为例,它的文件格式是csv,也是经常使用的一种文件存储格式,它是以逗号和换行来进行区分数据段和行来进行数据的区分。
今天我们把一个文件中的2000000条数据进行处理。

文件操作的几种方式
#include <stdio.h>
#include <Windows.h>
#include <clocale>

const UINT MAXLINESIZE = 1024;

wchar_t *Utf8ToUnicode(BYTE *strUtfData)
{
    int iLen = MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<char *>(strUtfData), -1, nullptr, 0);
    wchar_t *strRet = new wchar_t[iLen];
    MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<char *>(strUtfData), -1, strRet, iLen);
    return strRet;
}

BOOL ReadLine(const HANDLE hFile, BYTE *strBuf, UINT uMax = MAXLINESIZE)
{
    BOOL bRet = FALSE;
    BYTE byteTemp;
    UINT uIndex = 0;
    DWORD dwReadLen;

    while (ReadFile(hFile, &byteTemp, sizeof(BYTE), &dwReadLen, nullptr))
    {
        if (dwReadLen != sizeof(BYTE))
        {
            break;
        }

        if (byteTemp == '\n')
        {
            bRet = TRUE;
            break;
        }
        else
        {
            if (uIndex < uMax)
            {
                strBuf[uIndex++] = byteTemp;
            }
        }
    }

    return bRet;
}


void ReadFromFile()
{
    setlocale(LC_ALL, "chs");
    wchar_t *wstrFilePath = L"1.csv";
    HANDLE hFile = CreateFile(wstrFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (INVALID_HANDLE_VALUE != hFile)
    {
        DWORD dwPrevTime = GetTickCount();
        DWORD dwCount = 0;
        BYTE byteArray[MAXLINESIZE] = { 0 };
        //wchar_t *wstrSearchData = L"1990";

        while (ReadLine(hFile, byteArray))
        {
            wchar_t *wstrTemp = Utf8ToUnicode(byteArray);

            if (wcsstr(wstrTemp, L"1990") != nullptr)
            {
                ++dwCount;
            }

            memset(byteArray, 0, MAXLINESIZE);
        }
        DWORD dwCurTime = GetTickCount();
        DWORD dwTimeElaped = dwCurTime - dwPrevTime;

        wprintf(L"共找到:%d 条符合条件的记录, 用时:%d毫秒。\r\n", dwCount, dwTimeElaped);

        CloseHandle(hFile);
    }
    else
    {
        // 错误处理
        wchar_t wstrMessage[MAXBYTE] = { 0 };
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0, wstrMessage, MAXBYTE, nullptr);
        wprintf(L"打开文件失败,错误原因是:%s\r\n", wstrMessage);
    }
}

void ReadFromFileMap()
{
    setlocale(LC_ALL, "chs");
    wchar_t *wstrFilePath = L"1.csv";
    DWORD dwPrevTime = GetTickCount();
    DWORD dwCount = 0;

    HANDLE hFile = CreateFile(wstrFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (INVALID_HANDLE_VALUE != hFile)
    {
        HANDLE hFileMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
        if (hFileMap != nullptr)
        {
            char *pFileMapAddr = (char *)MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
            MEMORY_BASIC_INFORMATION memBuffer;
            VirtualQuery(pFileMapAddr, &memBuffer, sizeof(MEMORY_BASIC_INFORMATION));
            BYTE byteTemp[MAXLINESIZE] = { 0 };
            UINT uIndex = 0;
            for (UINT i = 0; i < memBuffer.RegionSize; ++i)
            {
                if (pFileMapAddr[i] != '\n')
                {
                    byteTemp[uIndex++] = pFileMapAddr[i];
                    if (pFileMapAddr[i] == '\0')
                    {
                        UnmapViewOfFile(pFileMapAddr);
                        break;
                    }
                }
                else
                {
                    wchar_t *pTemp = Utf8ToUnicode(byteTemp);
                    if (wcsstr(pTemp, L"1990") != nullptr)
                    {
                        ++dwCount;
                        //wprintf(L"%s", pTemp);
                    }
                    uIndex = 0;
                    memset(byteTemp, 0, MAXLINESIZE);
                    delete[] pTemp;
                }
            }
            UnmapViewOfFile(pFileMapAddr);
            CloseHandle(hFileMap);
        }
        else
        {
            wchar_t wstrMessage[MAXBYTE] = { 0 };
            FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0, wstrMessage, MAXBYTE, nullptr);
            wprintf(L"文件映射失败,错误原因是:%s\r\n", wstrMessage);
        }
        CloseHandle(hFile);
    }
    else
    {
        // 错误处理
        wchar_t wstrMessage[MAXBYTE] = { 0 };
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0, wstrMessage, MAXBYTE, nullptr);
        wprintf(L"打开文件失败,错误原因是:%s\r\n", wstrMessage);
    }

    DWORD dwCurTime = GetTickCount();
    DWORD dwTimeElaped = dwCurTime - dwPrevTime;

    wprintf(L"共找到:%d 条符合条件的记录, 用时:%d毫秒。\r\n", dwCount, dwTimeElaped);
}


void ReadFromMemory()
{
    setlocale(LC_ALL, "chs");
    wchar_t *wstrFilePath = L"1.csv";
    DWORD dwPrevTime = GetTickCount();
    DWORD dwCount = 0;

    HANDLE hFile = CreateFile(wstrFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (INVALID_HANDLE_VALUE != hFile)
    {
        // 获取文件大小
        LARGE_INTEGER largeInteger;
        if (GetFileSizeEx(hFile, &largeInteger))
        {
            char *pFilldata = new char[largeInteger.QuadPart];
            if (ReadFile(hFile, pFilldata, largeInteger.QuadPart, nullptr, nullptr))
            {
                BYTE byteTemp[MAXLINESIZE] = { 0 };
                UINT uIndex = 0;
                for (UINT i = 0; i < largeInteger.QuadPart; ++i)
                {
                    if (pFilldata[i] != '\n')
                    {
                        byteTemp[uIndex++] = pFilldata[i];
                        if (pFilldata[i] == '\0')
                        {
                            break;
                        }
                    }
                    else
                    {
                        wchar_t *pTemp = Utf8ToUnicode(byteTemp);
                        if (wcsstr(pTemp, L"1990") != nullptr)
                        {
                            ++dwCount;
                        }
                        uIndex = 0;
                        memset(byteTemp, 0, MAXLINESIZE);
                        delete[] pTemp;
                    }
                }

                delete[] pFilldata;
            }
            else
            {
                wchar_t wstrMessage[MAXBYTE] = { 0 };
                FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0, wstrMessage, MAXBYTE, nullptr);
                wprintf(L"读取文件内容失败,错误原因是:%s\r\n", wstrMessage);
            }
        }
        else
        {
            wchar_t wstrMessage[MAXBYTE] = { 0 };
            FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0, wstrMessage, MAXBYTE, nullptr);
            wprintf(L"获取文件大小失败,错误原因是:%s\r\n", wstrMessage);
        }

        CloseHandle(hFile);
    }
    else
    {
        // 错误处理
        wchar_t wstrMessage[MAXBYTE] = { 0 };
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(), 0, wstrMessage, MAXBYTE, nullptr);
        wprintf(L"打开文件失败,错误原因是:%s\r\n", wstrMessage);
    }

    DWORD dwCurTime = GetTickCount();
    DWORD dwTimeElaped = dwCurTime - dwPrevTime;

    wprintf(L"共找到:%d 条符合条件的记录, 用时:%d毫秒。\r\n", dwCount, dwTimeElaped);
}


int main()
{
    wprintf(L"普通的文件读取方式,一个字节一个字节的读取:\r\n");
    ReadFromFile();
    wprintf(L"\r\n\r\n文件映射方式:\r\n");
    ReadFromFileMap();
    wprintf(L"\r\n\r\n一次性将整个内容读取到内存再处理的方式:\r\n");
    ReadFromMemory();

    system("pause");
    return 0;
}

结果如下:
这里写图片描述

需要注意的是:
- wprintf函数输出宽字节的问题,如果我们直接输出宽字节的中文字符,它会输出乱码,此时就需要对编码进行设置,使用setlocale(LC_ALL, “chs”)函数来设置,就能正确输出中文字符;
- MultiByteToWideChar的Windows API函数的使用需要注意;
- 使用FileMap时,因为我的内存可以一次全部将322兆的文件读进来,但是如果碰到计算机内存很小的计算机,有可能一次就读不进去322兆的文件,上面的方法就会有问题。正确的方式应该分多次来读取,这个方法下次再介绍。
- 从三中结果来看,使用文件映射时间最短,而一个字节一个字节的读取时间最长,一次将整个文件读进到内存的时间略大于文件映射,我们几乎可以认为多出来的大概500毫秒的时间是将整个文件读到内存所用与文件映射所用时间的差,所以总的来说,文件映射还是很块的。其实我们可以单独做一个实验,将只读到内存和文件映射的耗费时间。

下面的结果是指读取,不处理
这里写图片描述
从结果看出,文件映射几乎是不需要时间的,所以在以后的工作中,我们可以将很多事情都用文件映射来处理,比如:画界面,我们可以把bmp直接加载到FileMap中,然后通过hdc来画FileMap中的数据。其实任何的数据都可以加载到FileMap中,但是也不要滥用,比如很小的文件就不用了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值