数据结构实验-大文件分成小文件-内存映射方法

 针对海量日志数据,提取出某日访问买票网站次数最多的那个IP

算法思路:批量生产500小文件,并且同时将它们打开,分块读大文件,如果每次将大文件的内容写进小文件再打开小文件会很耗时。用内存映射的方法打开大文件,创建视图对数据遍历同时模500放进对应的小文件。查找频率最大的方法是对每个小文件遍历的同时更新最大值和对应最大值的IP(包含相同频率),放进结构体中一个500个结构体对应每个小文件的结果。处理完全部小文件后遍历结构体找到频率最大的以及对应它出现在哪些文件中。最后把这些文件的最大频率的IP输出到结果。

// IP.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <windows.h>

#include <cstring>
#include <fstream>

#include <cstring>
#include <ctime>
#include <unordered_map>
#include <vector>

#include <iostream>

using namespace std;

#define ll long long

#ifdef UNICODE//统一编译码

#define fstring std::wstring

#else

#define fstring std::string

#endif 

char data_name[50];
FILE *fp[505];


struct IP_adress//文件结果结构体
{
	vector<ll> ip_v;
	int val;
	IP_adress()
	{
		val = 0;
	}
}IP_f[503];

ll get_num(char *str)//将字符串转换为整型
{
	ll num = 0;
	for (int i = 0;; i++)
	{
		if (str[i] == ' ' || str[i] == '\0')break;
		num = num * 10 + str[i] - '0';
	}
	return num;
}

void get_ip(ll num, int *ip)//得到ip的四个字段的值
{
	int tmp = 0;
	int cnt = 0;
	while (cnt<4)//每个字段处理
	{
		int x = 1;
		int k = 8;//每个字段每八位处理
		while (k--)
		{
			if (num & 1) tmp = tmp + x;
			x = x * 2;
			num >>= 1;
		}
		ip[cnt++] = tmp;
		tmp = 0;
	}
}

void solve()
{
	for (int i = 0; i < 500; i++)
	{
		unordered_map<ll, int>mp;//维护最大频率的IP和数目
		int Max = 0;
		vector<ll> Max_num;//得到与最大频率相同的ip
		char str_date[100];
		char str_time[100];
		ll num = 0;
		while(fscanf(fp[i], "%lld%s%s", &num, str_date, str_time)==3)
		{
			int tmp = ++mp[num];//对应ip数量加一
			if (Max < tmp)//更新最大值及IP
			{
				Max_num.clear();
				Max = tmp;
				Max_num.push_back(num);
			}
			else if (Max == tmp)//存最大值相同的IP
			{
				Max_num.push_back(num);
			}
		}
		IP_f[i].val = Max;//存进结构体
		IP_f[i].ip_v = Max_num;
	}
	cout << "success solve\n";
}

void result()
{
	FILE *tfp = fopen("data\\result.txt", "w");//创建结果文件,将结果写进文件里
	int Max = 0;
	vector<int> Max_id;//临时存频率最大的文件
	for (int i = 0; i < 500; i++)//查找最大值
	{
		if (Max < IP_f[i].val)
		{
			Max = IP_f[i].val;
		}
	}

	for (int i = 0; i < 500; i++)//得到最大频率所在的文件
	{
		if (Max == IP_f[i].val)
		{
			Max_id.push_back(i);
		}
	}

	fprintf(tfp, "最大频率:%d\n",Max);
	fprintf(tfp, "频繁最大的IP:\n");

	int s[5];
	for (int i = 0; i < Max_id.size(); i++)//输出频率最大的ip
	{
		int id = Max_id[i];
		for (int j = 0; j < IP_f[id].ip_v.size(); j++)
		{
			get_ip(IP_f[id].ip_v[j], s);//得到ip的四个字段
			fprintf(tfp, "%d.%d.%d.%d\n", s[0], s[1], s[2], s[3]);
		}
		
	}
	

	fclose(tfp);
	cout << "success result\n";
}

bool FileEncryption(fstring fsSrcPath, fstring fsDesPath)

{

	// 创建文件对象
	HANDLE hFile = CreateFile(fsSrcPath.c_str(), GENERIC_READ | GENERIC_WRITE,
		0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);// 参数意义:文件名,读/写访问,不共享文件,预设安全性,打开新文件或现有文件,常规文件属性,没有文件模板
	if (hFile == INVALID_HANDLE_VALUE)
	{
		cout<<"创建文件对象失败";
		return 0;
	}
	// 创建文件映射对象
	HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);//参数意义:文件句柄,预设安全性,对映射页面的读/写访问,映射整个文件,写入共享内存
	if (hFileMap == NULL)
	{
		cout<<"创建文件映射对象失败,错误代码:%drn";
		return 0;
	}

	// 得到系统分配粒度,系统的页面大小的倍数,节约内存
	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;

	char *buf = new char[100];
	int len = 0;
	int u = 0;

	if (qwFileSize < 1000 * dwGran)
		dwBlockBytes = (DWORD)qwFileSize;
	while (qwFileSize > 0)
	{
		// 访问映射视图
		LPBYTE lpbMapAddress = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS,
			(DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),
			dwBlockBytes);
		if (lpbMapAddress == NULL)
		{
			cout<<"映射文件全部映射完成";
			return 0;
		}
		// 对映射的视图内容进行访问
		char *a = (char *)lpbMapAddress;
		for (DWORD i = 0; i < dwBlockBytes; i++)
		{
				if (*(a + i) == '\n')//遇到换行就是一条数据
				{
					*(buf + len) = '\0';
					ll num = get_num(buf);
					int u = num % 500;
					fprintf_s(fp[u], buf);//写进文件
					len = 0;
				}
				else
				{
					*(buf + len) = *(a + i);
					len++;
				}

		}	

		// 撤消文件映像
		UnmapViewOfFile(lpbMapAddress);
		// 修正参数
		qwFileOffset += dwBlockBytes;
		qwFileSize -= dwBlockBytes;
	}
	// 关闭文件映射对象句柄
	CloseHandle(hFileMap);
	cout<<"成功完成对文件的访问";

	return 0;

}

int main()
{
	clock_t start, finish;//测试运行时间
	start = clock();

	for (int i = 0; i < 500; i++)//打开小文件
	{
		sprintf(data_name, "%s%d%s", "data\\data_", i, ".txt");//批量生产小文件
		fp[i] = fopen(data_name, "a+");
	}

	FileEncryption(L"E:\\cproject\\IP\\IP\\data5e5.txt", L"");//打开大文件

	for (int i = 0; i < 500; i++)//将小文件的指针重置
	{
		rewind(fp[i]);
	}

	solve();//处理结果
	result();

	finish = clock();
	cout << ((double)(finish - start) / CLOCKS_PER_SEC) << " (s) " << endl;//打印运行时间

	for (int i = 0; i < 500; i++)//关闭小文件
	{
		fclose(fp[i]);
	}

	system("PAUSE");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39132605/article/details/91904010