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

来源:互联网 发布:我怎么投诉淘宝店铺 编辑:程序博客网 时间:2024/06/08 08:47

大文件数据操作

我们以全国开放数据(数据纯属伪造)为例,它的文件格式是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中,但是也不要滥用,比如很小的文件就不用了。