14海量日誌提取出現次數最多的IP
問題描述:現有某網站海量日誌資料,提取出某日訪問該網站次數最多的那個IP。
分析:IP地址是32位的二進位制數,所以共有N=2^32=4G個不同的IP地址, 如果將每個IP地址看做是陣列的索引的話,那麼需要建立一個unsigned count[N]的陣列,即可統計出每個IP的訪問次數,但是這個陣列的大小是4G*4=16G, 遠遠超過了32位計算機所支援的記憶體大小,因此不能直接建立這個陣列。
採用劃分法解決這個問題,假設允許使用的記憶體是512M,512M記憶體可以統計128M個不同的IP地址的訪問次數。而4G/128M = 32,所以只要把IP地址劃分成32個不同的區間,分別統計出每個區間中訪問次數最大的IP,然後就可以計算出所有IP地址中訪問次數最大的IP了。
可以把IP地址的最高5位作為區間編號, 剩下的27位作為區間內的值,建立32個臨時檔案,代表32個區間,把相同區間的IP地址儲存到同一的臨時檔案中。
例如:ip=0x1f4e2342,高5位id=ip>>27=0x11=3,低27位是value=ip&0x07ffffff = 0x074e2342。所以,當掃描到IP為0x1f4e2342時,將value儲存在tmp3檔案中。
按照上面的方法掃描海量日誌,可以得到32個臨時檔案,每個臨時檔案中的IP地址的取值範圍屬於[0-128M),因此可以統計出每個IP地址的訪問次數。從而找到訪問次數最大的IP地址。
程式碼如下:
#define N 32 //臨時檔案數
#define ID(x) (x>>27) //x對應的檔案編號
#define VALUE(x) (x&0x07ffffff) //x在檔案中儲存的值
#define MAKE_IP(x,y) ((x<<27)|y) //由檔案編號和值得到IP地址.
#define MEM_SIZE 128*1024*1024
char * data_path = "D:/test/ip.dat"; //ip資料
//產生n個隨機IP地址
void make_data(const int& n)
//找到訪問次數最大的ip地址
int main()
{
make_data(100000000); //產生測試用的IP資料
fstream arr[N];
for (int i=0; i<N; ++i) //建立N個臨時檔案
{
char tmp_path[128];
sprintf(tmp_path,"D:/test/tmp%d.dat",i);
arr[i].open(tmp_path,ios::trunc|ios::in|ios::out|ios::binary); //開啟第i個檔案
if( !arr[i])
{
cout<<"openfile"<<i<<"error"<<endl;
}
}
ifstreaminfile(data_path,ios::in|ios::binary); //讀入測試用的IP資料
unsigned data;
while(infile.read((char*)(&data),sizeof(data)))
{
unsigned val=VALUE(data);
int key=ID(data);
arr[key].write((char*)(&val),sizeof(val)); //儲存到臨時檔案件中
}
for(unsigned i=0; i<N; ++i)
{
arr[i].seekg(0);
}
unsigned max_ip = 0; //出現次數最多的ip地址
unsigned max_times = 0; //最大隻出現的次數
//統計每個數出現的次數
unsigned *count = newunsigned[MEM_SIZE];
for (unsigned i=0; i<N; ++i)
{
memset(count, 0,sizeof(unsigned)*MEM_SIZE);
//統計每個臨時檔案件中不同數字出現的次數
unsigned data;
while(arr[i].read((char*)(&data),sizeof(unsigned)))
{
++count[data];
}
//找出出現次數最多的IP地址
for(unsigned j=0; j<MEM_SIZE;++j)
{
if(max_times<count[j])
{
max_times = count[j];
max_ip = MAKE_IP(i,j); // 恢復成原ip地址.
}
}
}
unsigned char *result=(unsigned char *)(&max_ip);
printf("出現次數最多的IP為:%d.%d.%d.%d,共出現%d次", result[0],result[1], result[2], result[3], max_times);
}
(http://blog.csdn.net/v_july_v/article/details/6712171)