小试大数据处理

这些思路网上都有,就是自己实践一下。ps:昨晚写了40多分钟的东西,本来想发的,后来崩溃了...> . <  , 以后在也不能在网页上直接写了

/*
 * =====================================================================================
 *
 *       Filename:  find_the_same.c
 *
 *    Description:  查找两个文件中相同的用户名(下面的用户名是用26个小写字母随即组成的)
 *					主要的思路:
 *						1) 两个文件都是billions级的数字 1000,000,000 * 28(字符串结束赋和分割符) bytes
 *						    26G 的大小,这是没有办法一次性导入并做比较的。
 *						    所以第一步就是把大的文件 分割成 小的文件,没个小的文件都是由一个标志符决定,
 *						    为了能确定 a 和 b相同的用户名一定存在同个等级的小文件,因为这样就不需要不同级别
 *						    的小文件在进行比较
 *						2) 为了完成第1)步后面的要求,要构建一个哈希函数,同个username在 同个哈希函数的映射下
 *						    并定存在同个等级的小文件
 *						    因此,第二步,就是确定 分割大文件的哈希函数,我这里这是简单的进行一些四则运算,只要
 *						    能把大文件打散就行了
 *						3) 在前面两步的基础上,
 *							就是进行小文件之间的比较,这里我用的是哈希查找:
 *							1)先导入第一个小文件的所有username, 并建立一个哈希链表
 *							2)再从第二个小文件逐步取出一定的username,再哈希表中进行查找,如果存在的话就把结果
 *							   取出来,这里是直接输出到stdout上
 *							注意:这里哈希表的函数必须和上面的不同
 *
 *						以上就是我用于解决这道题的思路
 *        Version:  1.0
 *        Created:  08/18/2012 12:27:34 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  lsff  (刘世锋)
 
 *   Organization:  
 *
 * =====================================================================================
 */

// 代码运行:
// 我的测试是通过生成 a.dat 和 b.dat 两个大文件,让后在运行的目录下先家里a 、b 两个目录,用于存放小文件的
// 所以如果 有测试数据的, 就把 数据 命名位 a.dat 和 b.dat , 然后把源程序可执行文件放到你文件的目录下运行
// 第一次运行把#undef  DIVIVE 注释掉,第二次运行是就可以取消注释
//
// 如果没有测试数据,就把 #undef WRITE 注释掉,然后就会自动生成数据,数据的到小可以在 注释掉,第二次运行是就可以取消注释
//
// 如果没有测试数据,就把 #undef WRITE 注释掉,然后就会自动生成数据,数据的到小可以在 testData()函数里面修改
// 注意!!! 生成数据的时间会比较长
//
//

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define  WTESTBUF 1000   //写测试文件时候的缓冲大小
#define  READBUF  1000   //读大文件时候的读取缓冲区大小
#define  STYPES  1000    //分割文件哈希函数生成的数值类别数
#define  SIZE  100       //分割文件是每个小文件待写入前的缓冲区
#define  LINKSIZE 1000   // 哈希表的长度,也就是hash2 哈希函数产生的值范围

#define  WRITE  //写测试数据
#undef WRITE    // 已写测试数据   !!!!如果还没有生成测试数据,就注释掉这一行
#define  DIVIVE// dive 文件
#undef DIVIVE  //已dive 大文件    !!!!如果还没有分割大文件,就注释掉这一行

// 哈希函数(用于分割文件的)
// usr_name 长度不超过27
// return : 0~999 之间
unsigned short hash( char * usr_name)
{
	unsigned short sum=0;
	int i=0;
	for(;i<strlen(usr_name);i++)
	  sum += i*usr_name[i];
	return sum%1000;
}


// 哈希函数(用于建立查找链表的)
// return : 0~999 之间
unsigned short hash2( char *usr_name)
{
	unsigned short sum=0;
	int i=0;
	for(;i<strlen(usr_name);i++)
	  sum += i*i*usr_name[i];
	return sum%1000;
}

// 存储用户名的结构体
// 可在里面加成员进行扩展
typedef struct 
{
	char name[28];
}usr;


// 哈希表的节点
typedef struct lnote 
{
	usr value;
	struct lnote* next; 
}lnote;

lnote* hash_link[LINKSIZE]={};// 哈希链表,用于查找两个10m大小级别文件中相同的usr

// 在哈希表中插入某个节点
void add_lnote( unsigned short index, usr adder)
{
	if( NULL == hash_link[index])
	{
		hash_link[index]= malloc(sizeof(lnote));
		bzero(hash_link[index], sizeof(lnote));
		hash_link[index]-> value = adder;
		hash_link[index]-> next = NULL;
		return ;
	}
	lnote* tmp = hash_link[index], *pre;	
	while( tmp!=NULL)
	{
		if( 0==strcmp( tmp->value.name, adder.name)) return ;
		pre=tmp; 
		tmp=tmp->next;
	}
	pre->next =  malloc( sizeof(lnote) );
	bzero(pre->next,sizeof(lnote));
	(pre->next)->value =adder;
	(pre->next)->next=NULL;
}

//第二个文件查找usrname是否已存在
//存在 返回0 ,并删除该链表项,减少下次查找次数
//不存在 返回-1
int  find_lnote( int index ,usr finder)
{
	if(NULL==hash_link[index]) return -1;
	lnote* pre=hash_link[index], *cur=hash_link[index];
	while(1)
	{
		if(0==strcmp( cur->value.name, finder.name))
		{
			if(cur == hash_link[index]) hash_link[index]=cur->next;
			else pre->next= cur->next;
			return 0;
		}
		pre=cur; cur=cur->next;
		if( cur==NULL) break;
	}
	return -1;
}

// 删除哈希表申请的堆空间
void delete_link()
{
	int i=0;
	lnote *pre, *tmp;
	for(;i<LINKSIZE;++i)
	{
		pre=tmp=hash_link[i];
		while(tmp!=NULL) { pre=tmp->next; free(tmp); tmp=pre; }
	}
}


// 这个函数是用于生成测试数据用的
// fname: 存发生成的数据的文件名
// sand: 随机种子
void testData(const char *fname, int sand)
{
	usr *buf = calloc(WTESTBUF, sizeof(usr));
	FILE* file = fopen(fname, "a+");
	int i=0, num=0x00ffffff; //先测试小的 
	if( NULL==buf) { puts("calloc error!"); return ;}
	if( NULL==fname ) {puts("file name error!");return;}
	if( NULL==file ) {puts("open file error!"); return ;}

	srand(sand);
	int j;	
	for( ;i<num;i++)
	{
		if(0==(i+1)%WTESTBUF )  // 生成 WTESTBUF个用户名在输入文件,减少IO操作时间
		{
			if(fwrite(buf,sizeof(usr),WTESTBUF,file)!=WTESTBUF) { puts("generate testData error!"); return ; }
			bzero(buf,WTESTBUF*sizeof(usr));
		}
		for(j=0;j<26;j++)  buf[i%WTESTBUF].name[j]= 'a'+ rand()%26; 
		buf[i%WTESTBUF].name[26]='\0'; buf[i%WTESTBUF].name[27]='\n';
	}

	if( num%WTESTBUF  != 0 && fwrite(buf, sizeof(usr), num%WTESTBUF,file)!= num%WTESTBUF)   //剩余缓冲的继续写入
	{
		puts("Last generate testData error!");
		return ;
	}
	free(buf); buf=NULL; 
	fclose(file);file=NULL;
	printf("Write successfully! %d \n", num);
}


// 把特定的用户名写入小文件中
void write_sort_file(char *filename, usr* buf, size_t t)
{
	if( NULL == buf ) { puts("write sort file error!"); return ;}	
	if( NULL == filename) { puts("file name error!");return ;}
	FILE *file = fopen(filename, "a+");
	if( NULL == file) {puts(filename); puts("open sort file error!");return ;}
	if( t>0 )
	  if( fwrite( buf, sizeof(usr), t, file)!=t)
		puts("write sort file error!");
	fclose(file);
}

// filename: 待分割的大文件
// folder: 分割之后的小文件存储的目录
void sort_by_hash(const char* file_name, char *folder)
{
	usr *rbuf = calloc(READBUF, sizeof(usr)); // 用于从大文件中接入的用于名缓冲区,一次缓存的大小是READBUF(1000)个用户
	usr *sortbufs[STYPES];					  // 存储分割小文件的数组,元素是一个指向usr的指针,可以存储一串待存入小文件的特定数据
	char *sort_names[STYPES];                 // 存储每个小文件特定的文件名
   	size_t cur_sizes[STYPES];				  // 记录当前各个小文件缓冲区中存储的用户个数,达到SIZE个数就输入各自的小文件
	if( NULL==rbuf) { puts("calloc error!"); return ;}
	FILE* file = fopen(file_name, "r");
	int i=0,tmpSize, hasNum;
	if( NULL==file_name )  { 
		printf("%s %d", __FUNCTION__, __LINE__);
		puts("file name error!"); free(rbuf);return;}
	if( NULL==file ) {
		printf("%s %d\n", __FUNCTION__, __LINE__);
		puts("open file error!"); free(rbuf); return ;}

	bzero(sort_names, sizeof(sort_names));
	bzero(cur_sizes, sizeof(cur_sizes));
	bzero(rbuf, sizeof(usr)*READBUF);
	for(i=0;i<STYPES;i++){
		sort_names[i]= malloc(sizeof(char)*15);
		sprintf(sort_names[i],"%s/%s%d",folder,file_name, i);				
	   	sortbufs[i] = calloc(SIZE, sizeof(usr));
	}
	
	while((tmpSize= fread( rbuf, sizeof(usr), READBUF, file)) >0)
	{
		for(i=0;i<tmpSize;i++)
		{
			hasNum =  hash( rbuf[i].name);
			memcpy( &(sortbufs[hasNum][cur_sizes[hasNum]]), &rbuf[i], sizeof(usr));
			++cur_sizes[hasNum];

			if(SIZE==cur_sizes[hasNum])  // 同样也是减少IO 的操作时间
			{
				write_sort_file(sort_names[hasNum], sortbufs[hasNum], cur_sizes[hasNum]);
				cur_sizes[hasNum]=0;	
				bzero(sortbufs[hasNum], sizeof(usr)*SIZE);
			}
		}		

		bzero(rbuf,READBUF*sizeof(usr));
	}
	for(i=0;i<STYPES; i++)
	{
		if(!cur_sizes[i]) continue;
		write_sort_file(sort_names[i], sortbufs[i],cur_sizes[i]);
	}
	
	for(i=0;i<STYPES; i++)
	{
		free (sort_names[i]);
		free (sortbufs[i]);
	}
	fclose(file);
	free(rbuf);
	file=NULL; rbuf=NULL;
}

// 根据filename 和 i 组合的小文件读入的数据建立哈希表
// 这是我上面生成小文件的规则,就是源文件名加上哈希值
void create_hash_link(char *filename, int i)
{
	delete_link(); //如果是多对小文件进行匹配,就需要把前一对的生成的哈希表释放
	bzero(hash_link,sizeof(hash_link));

	if(!(filename!=NULL && i>=0 &&i<=999)){
		printf("%s %d filename error!", __FUNCTION__, __LINE__);
		return ;
	}
	char str[30];
	sprintf(str,"%s%d", filename, i);
	FILE* file = fopen(str, "r");
	if( NULL==file ) {
		printf("%s %d  open file  error!", __FUNCTION__, __LINE__);
		return ;
	}
	usr users[200];
	size_t t=0,j=0;
	unsigned short hashNum=0;
	while( (t=fread(users, sizeof(usr), 200, file))>0)
		for(j=0;j<t;++j)
		{
			hashNum = hash2(users[j].name);
			add_lnote(hashNum, users[j]);	
		}
	fclose(file);
	file=NULL;
}


// 在导入前一个文件后并且建立了一个哈希表之后
// 用第二个同位文件(分割文件时候哈希码相同)的
// 名字在哈希表上进行查找
void find_in_link(char *filename ,int i)
{
	if(!(filename!=NULL && i>=0 &&i<=999)){
		printf("%s %d filename error!", __FUNCTION__, __LINE__);
		return ;
	}
	char str[30];
	sprintf(str,"%s%d", filename, i);
	FILE* file = fopen(str, "r");
	if( NULL==file ) {
		printf("%s %d  open file  error!", __FUNCTION__, __LINE__);
		return ;
	}
	usr users[200];
	size_t t=0,j=0;
	unsigned short hashNum=0;
	while( (t=fread(users, sizeof(usr), 200, file))>0)
		for(j=0;j<t;++j)
		{
			hashNum = hash2(users[j].name);
			if(!find_lnote( hashNum, users[j]))
				printf("%s\n", users[j].name);
		}
	fclose(file);
	file=NULL;
}

int main()
{
#ifdef WRITE
	testData("a.dat",123);
	testData("b.dat",321);
#endif



#ifdef DIVIVE
	sort_by_hash("a.dat","a");
	sort_by_hash("b.dat","b");
#endif
	bzero(hash_link, sizeof(hash_link));
	
	int i=0;
	for(i=0;i<STYPES;i++)
	{
		create_hash_link("a/a.dat",i);
		printf("The same name between a.dat%d and b.dat%d:\n", i,i);
		find_in_link("b/b.dat",i);
	}
	delete_link();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值