微项目之文本词频统计(第一版)

题目:统计文本中重复次数最多的前1000的单词,并按次数从高到低输出,并保存到另一个文本中



题目这里说明一下,是对英文文本进行操作,所以这里是在http://novel.tingroom.com/ertong/4017/随意下了一篇英文小说,名字叫 A 《Sweet Little Maid》



这个题目要解决两个问题,第一是如何将仅读单词然后划分开并且保存,第二是保存以后如何去重。
这里暂且创建两个结构体,一个通过结构体创建结构体数组,每一个存储一个单词,此处并不去重,然后另一个也是创建结构体数组,但是这个是字典创建,并且有计数的变量,具体结构体如下

typedef struct wcount//读入单词结构体
{
	char word[20];
};
typedef struct worddic//字典结构体
{
	char word[20];
	int count;
};

因为这里创建的是字符数组,所以分别写一个初始化函数,字符数组初始化为空字符串,计数器初始化为0。实现如下

void init(wcount *wc,const int len)//初始化读入单词的字符串
{
	for(int i=0;i < len; ++i)
	{
		wc[i].word[0] = '\0';
		
	}
}
void initd(worddic *wd,int len)//初始化字典的字符串
{
	for(int i=0;i < len; ++i)
	{
		wd[i].word[0] = '\0';
		wd[i].count = 0;
	}
}

然后就是对文本的操作了,打开文件,读取数据关闭文件。在当中要计算一下文本大小,以此来选择动态内存的调用大小

void dofile(const char *path,const char *despath)//文件操作
{
	FILE *fp = fopen(path,"rb");
	assert(fp != NULL);

	fseek(fp,0,SEEK_END);//光标移至文本尾
	int len = ftell(fp)/sizeof(char);//计算大小
	fseek(fp,0,SEEK_SET);//恢复光标,至文本头

	wcount *wc = (wcount *)malloc((len/4)*sizeof(wcount));
	worddic *wd = (worddic *)malloc((len/4)*sizeof(worddic));//动态内存创建
	}

因为题目是将处理结果保存至另一个文本中,所以这里有目标文件的存储地址,还有要存在哪里,这里需要说的就是,使用fseek()函数将光标移动到文本结尾,计算完文本大小,容易忘记将光标移动回文本头,会让接下来读文件内容时无法读取


接下来时读取、划分、存储单词,代码实现如下

void saveword(wcount *wc,char word ,FILE *fp)//将单词读取至结构体的word中
{
	int i = 0;
	int j = 0;
	while(fread(&word,sizeof(char),1,fp) > 0)
	{
		if(word == '\n' || word == '\r')//跳过换行
		{
			continue;
		}
		if(jud(word))//字母赋值给单词结构体
		{
			wc[i].word[j] = word;
			j ++;
		}
		else if(!jud(word))
		{
			wc[i].word[j] = '\0';//将上述单词加结尾标记,使其成为字符串
			if(strlen(wc[i].word) == 0)//处理遇到连续非字母字符,直接跳过
			{
				continue;
			}
			else //一个单词读取完成,结构体跳至下一个,存储单词的word数组脚标重置
			{
				++ i;
				j = 0;
			}
		}
	}
	wc[i].word[j] = '\0';//最后一个单词变为字符串
}

这里本来是通过库函数isalpha()函数来判断当前读入字符是否为字母,但是在运行时候总是断言失败,所以就自行编写jud()函数进行简易判断。
这里说一下大概的存储思路,若是当前字符为字母,则将他赋值给当前结构体的word变量的0号下表,然后下标加一,若是当前字符不是字母,则把当前word变量最后一个赋值为’\0’,使其变为字符串,若是当前非字母,并且当前结构体的word为空串,说明遇到连续非字符,所以当前word变量不进行赋值等待并且字母字符,若是当前非字母,并且当前word不是空串,说明一个单词读取结束,所以word变量跳至下一个结构体的word变量,并且下标计数器恢复为0,最后的最后把最后一个单词也变为字符串。这样就把单词全都存入结构体中了。
接下来就是字典的创建了,实现代码如下

void savedic(const wcount *wc,worddic*wd)//字典创建
{
	int i = 0;
	int j = 0;
	int MAX = 0;
	for(i = 0;strlen(wc[i].word)!=0;++i)
	{
		for(j = 0;j < MAX;++j)
		{
			if(strcmp(wc[i].word,wd[j].word)!=0)
				continue;
			break;
		}
		if(j == MAX)
		{
			strcpy(wd[j].word,wc[i].word);
			++ MAX;
		}
	}
}

写循环,判断条件为若是单词结构体的word非空串,就继续循环,i++,定义一个MAX变量来确定字典的边界,因为每有一个新单词时候,边界就要加一,所以这里MAX是动态的,在边界内对每个进来的单词进行比较,若是不相等,则继续比较,一旦相等,就跳出此趟比较。祠堂比较结束,最里层循环外有一个判断语句,对跳出来的下标与边界进行比较,若是相等,说明此趟比较已经比较结束,并且没有发现当前词典中与该单词相等的单词,说明是新单词,所以将该单词加入到词典中,边界加一,循环结束,词典创建结束。
词典创建结束,剩下的工作就好办了,对照词典进行词频统计实现代码如下

void count(const wcount *wc,worddic *wd)//对照字典进行计数
{
	for(int i = 0;strlen(wc[i].word)!=0;++i)
	{
		for(int j = 0;strlen(wd[j].word)!=0;++j)
		{
			if(strcmp(wc[i].word,wd[j].word)==0)
			{
				wd[j].count ++;
				break;
			}
			
		}
	}
}

若是单词与词典中一个单词相同,该单词的计数器加一,跳出,后续单词不进行比较,以此加快效率
然后就是词频排序了,这个就是简单的排序,不做多说明

void countsort(worddic *wd)//次数排序
{
	int tmp;
	char ttmp[50];
	for(int i = 0;strlen(wd[i].word)!=0;++i)
	{
		for(int j = i+1;strlen(wd[j].word)!=0;++j)
		{
			if(wd[i].count < wd[j].count)
			{
				tmp = wd[i].count;
				wd[i].count = wd[j].count;
				wd[j].count = tmp;//次数交换
				strcpy(ttmp,wd[i].word);
				strcpy(wd[i].word,wd[j].word);
				strcpy(wd[j].word,ttmp);//字符串交换
			}
		}
	}
}

若是定义一个保存字符串的中间变量,定义一个保存词频的中间变量,然后把词频大的换到最前边
然后就是结果打印了
读取前一千的词典结构体数组的值,然后输出至结果文本中,代码实现如下

void write1000(const char *despath ,worddic *wd)//写前一千的单词,并保存至目标路径
{
	FILE *fd = fopen(despath,"wb");
	assert(fd != NULL);
	for(int i = 0;i < 1000;++i)
	{
		fprintf(fd,"%s,%d",wd[i].word,wd[i].count);
		fputs("\n",fd);
	}
	fclose(fd);
}

这个小项目也算完成了,不过此项目的代码目前还没有进行修改简化,后续有时间简化提炼之后,我会把第二版也发上来。
这个小程序还需要一个小操作就是小说下载后,此小说结尾是直接结尾,所以我在后边加了一个空格,方便读入操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值