VC 大文件读取

个别时候,我们要读取非常大的文件,如 5GB 要把文件读出,再写进,耗费的是CPU利用率与内存以及IO的频繁操作。这显然是
令用户难以忍受的

为了解决这个吃内存,占CPU,以及IO瓶颈,windows核心编程提供了内存映射文件技术;

因为数据非常大,还要处理,我采用了,内存映射+线程池;

先看一下内存映射读数据:

#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;

string GetValue(const TCHAR *, const TCHAR *);  //根据name得value
void main(int argc, char* argv[])
{
    // 创建文件对象(C: est.tsr)
    HANDLE hFile = CreateFile("C:/test.tsr", GENERIC_READ | GENERIC_WRITE,
        0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("创建文件对象失败,错误代码:%d ", GetLastError());
        return;
    }
    // 创建文件映射对象
    HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
    if (hFileMap == NULL)
    {
        printf("创建文件映射对象失败,错误代码:%d ", GetLastError());
        return;
    }
    // 得到系统分配粒度
    SYSTEM_INFO SysInfo;
    GetSystemInfo(&SysInfo);
    DWORD dwGran = SysInfo.dwAllocationGranularity;
    // 得到文件尺寸
    DWORD dwFileSizeHigh;
    __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
    qwFileSize |= (((__int64)dwFileSizeHigh) << 32);
    // 关闭文件对象
    CloseHandle(hFile);
    // 偏移地址 
    __int64 qwFileOffset = 0;
    // 块大小
    DWORD dwBlockBytes = 1000 * dwGran;
    if (qwFileSize < 1000 * dwGran)
        dwBlockBytes = (DWORD)qwFileSize;
    if (qwFileOffset >= 0)
    {
        // 映射视图
        TCHAR *lpbMapAddress = (TCHAR *)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS, 
            0, 0,
            dwBlockBytes);
        if (lpbMapAddress == NULL)
        {
            printf("映射文件映射失败,错误代码:%d ", GetLastError());
            return;
        }

        
//-----------------------访问数据开始-------------------------
        cout<<GetValue(lpbMapAddress,"SEU07201213")<<endl;
        getchar();
//-----------------------访问数据结束-------------------------        
    
        // 撤销文件映像
        UnmapViewOfFile(lpbMapAddress);
    }
    // 关闭文件映射对象句柄
    CloseHandle(hFileMap);    
}
string GetValue(const TCHAR *lpbMapAddress, const TCHAR *sName)
{
   string sValue;  // 存放 = 后面的value值
  TCHAR *p1 = NULL, *p2 = NULL; // 字符指针
  if((p1 = strstr(lpbMapAddress,sName)) != NULL) // 查找sName出现位置
  {
   if(p2 = strstr(p1,"/r/n")) *p2 = '/0'; // 查找"/r/n"(换行)出现位置
   sValue = p1+strlen(sName)+strlen("="); // 指针移动"sName"+"="之后
   *p2 = '/r';  // 还原*p2值,因为不还原会改变原文件结构
  }
  return sValue;
}

以上实现了根据索引name匹配value的简单过程,经测试,同样25W行文件,匹配耗费1秒不到,且
不占本进程内存。

以上因为没有直接使用读入内存再处理的方式,大大节约了内存;再就是采用这种方式,减少了IO,时间也大大减少;

但 5GB的数据,如果这样读加处理,还是要4分钟左右;

所以我加上了线程池,这样,最后的效果是 40秒左右,勉强可以接受;

void GetFloatDataByPool(vtkPoints * points, char * fileBuffer, int count, int jump)
{
	char midData[50] = { 0 };
	int index = 0;
	double arr[3];

	for (int j = 0; j < count * jump; j++)
	{
		fileBuffer = (char*)memchr((void*)fileBuffer, '\n', 50);
		fileBuffer++;
	}
	for (int i = 0; i < jump; i++)
	{
			for (int  j =0; j < 3; j++)
			{
				arr[j] = atof(fileBuffer);
				fileBuffer = (char*)memchr((void*)fileBuffer, ' ', 10);
				fileBuffer++;
			}

			fileBuffer = (char*)memchr((void*)fileBuffer, '\n', 50);
			fileBuffer++;

			points->InsertPoint(index, arr[0], arr[1], arr[2]);
			index++;
	}
}
 ReadPtsByThreadPool()
{
	ThreadPool * executor = ThreadPool::GetInstance();
 
	//------------------------------------------------------内存映射方法;
	 
	//创建或打开文件内核对象;
	HANDLE fileH = CreateFile("D:\\workspace\\sj\\aa.pts",
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (fileH == INVALID_HANDLE_VALUE)
	{
		cout << "error in CreateFile" << endl;
		//return -1;
	}

	//创建一个文件映射内核对象;
	HANDLE mapFileH = CreateFileMapping(fileH,
		NULL,
		PAGE_READWRITE,
		0,
		0,
		"Resource ");
	if (mapFileH == NULL)
	{
		cout << "error in CreateFileMapping" << endl;
		//return -1;
	}

	//将文件数据映射到进程的地址空间;
	char * mapH = (char *)MapViewOfFile(mapFileH,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		0);
	if (mapH == NULL)
	{
		cout << "error in MapViewOfFile" << endl;
		//return -1;
	}

	//读取数据;
	char *fileBuffer = mapH;
	
	int times = atoi(fileBuffer);//300000 * 34;
	fileBuffer = (char*)memchr((void*)fileBuffer, '\n', 50);
	fileBuffer++;

	int dividCount = times / 4;

	std::future<void> ft[4];
	for (int s = 0; s < 4; s++)
	{
		char *tp1 = fileBuffer;
		ft[s] = executor->AddWork(GetFloatDataByPool, mSubPoints[s], tp1, s, dividCount);
	}

	for (int s = 3; s >=0; s--)
	{
		ft[s].get();
	}

	//for (int index = 0; index < dividCount * 4; index++)
	//{
	//	points->InsertPoint(index, arr[0], arr[1], arr[2]);
	//}
	//points->InsertPoint(index, arr[0], arr[1], arr[2]);
	//index++;

	//关闭句柄;
	UnmapViewOfFile(mapH);
	CloseHandle(mapFileH);
	CloseHandle(fileH);
}

完整代码样例:

https://download.csdn.net/download/q610098308/14987049

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恋恋西风

up up up

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值