基于boost正则表达式:对目录下所有文本文件的中文词频统计

这篇博客介绍了如何利用Boost库的正则表达式功能,对C盘ceshi目录下所有文本文件进行中文词频统计。首先从ciku.ini词库中获取常用词汇,然后在命令行运行程序tongji.exe,分析结果保存在output.ini文件中。程序实现了从txt文件中提取中文词汇并进行统计的功能。
摘要由CSDN通过智能技术生成

                首先得得到我们的常用词频,保存在ciku.ini中里面保存32280个词汇,保存形式如下

至于怎么得到词库,办法各异了,我这里是从xp自带的拼音和五笔输入法的码表文件中提取的词汇,去除两个码表相同的词汇,得到的总词汇有32280个,

1)在C盘建立ciku文件夹,拷贝“词库提取”文件夹中的词库文件ciku.iniciku目录,最终得到的结果也会在C盘的ciku下面的output.ini里面,保存形式如下。

[MyWord]
祖父=275
一个=144

。。。。
2)在C盘新建ceshi(可以是任意目录,只分析里面的txt文件)文件夹,该文件夹放入需要进行词频统计的文档,文档可以为多个;

3)在命令行下运行统计程序tongji.exe,后面的参数就是要检索的目录(也可以在命令行下输入帮助参数  \? 获取帮助信息)

运行效果如下:

开始运行的效果如下:  也可以直接在程序后面将目录路径作为参数传入

 

代码如下:

#include <windows.h>
#include <boost/regex.hpp>
#include <string>
#include <iostream>
#include <fstream>
#define MAXCI  32280
#define  MAXLEN   204800  //每次读取不大于20k的行

using namespace std;
using namespace boost;
void  RegFile(const char * filename);
void  SortInto();

typedef struct WordRate
{
	string word;
	int rate;

} Word;
//装载所有的词的数组
Word MyWord[33000];

//标志词库里面的词是不是已经全部读到了内存
bool ReadFlag=true;

//程序开始的时间
int starttime;

void main(int agrc,char *argv[])
{
	string Dir;			//要搜索的目录的路径

	starttime=GetTickCount();	//记下程序运行的开始时间
	string Fullname;
	string Filename;		//单个文件的名称

	WIN32_FIND_DATA  FileInfo;	//这个结构体 存储了检索到的文件的信息 (文件大小,名称,时间等信息)

	HANDLE  FileHandle;		//文件句柄

	if(agrc != 1)			//agrc 是我们在命令行里面传入的参数的个数,如果没有参数 argc就是1
	{				//如果有参数就将它存入Dir这个string变量
		Dir.assign(argv[1]);
	}
	else				//参数个数为零就提示输入
	{
	   cout<<"请输入要搜索的目录\n如:C:\\test\\*  \nc:\\test 就是你要检索的目录或者带帮助参数 \\? 看看吧"<<endl;
	   cin>>Dir;
	   //exit(0);
	}

	if(  Dir =="\\?"  )
	{
	   cout<<"-------------中文词频统计程序-------------"<<endl<<endl;
	   cout<<"--------传入的参数应该如:C:\\test\\*----------"<<endl;
	   cout<<"--------词库文件ciku.ini和最后的结果都保存在C盘的ciku文件夹下----------"<<endl;
	   cout<<"--------所以先在C盘建立一个ciku目录将词库文件ciku.ini拷贝进去----------"<<endl;
	   exit(0);
	}


	   //argv[1]就是我们在命令行里面传入的参数,不允许是C盘根目录,*号表示改目录下面的所有内容
	   FileHandle=::FindFirstFile(Dir.c_str(),&FileInfo);	
		
	   
	   //删除参入参数里面最后一个*符号,作为文件的目录名称,以便和后面找到的文件的名称拼起来,
	   Dir.erase(Dir.length()-1,1);	

	   printf ("Target Dir is %s \n",Dir.c_str());

	   //如果打开第一个文件失败,就直接退出
	   if(FileHandle==INVALID_HANDLE_VALUE)		
	   {
		   cout<<"打开目录失败 \n输入目录如:c:\\test\\* \n c:\\test\\* 就是你要检索的目录"<<endl;
		   return  ;
	   }

	 //如果能找到第一个文件,就根据文件检索句柄搜索目录下所有文件,	
	while(  FindNextFile(FileHandle,&FileInfo) != 0 )	
	{											 
		  //这里将见多到的文件名和路径拼接起来,组成一个fullname,
		  Fullname=Dir;			
		  Filename.assign(FileInfo.cFileName);

		  Fullname.append(Filename);
		  if( -1 == Filename.find(".txt") || 4 > ((FileInfo.nFileSizeHigh * (MAXDWORD +1)) + FileInfo.nFileSizeLow)  )  
		  {	
			   //只检索目录下的文本文件,如果文件大小为小于4个字节,也不进行匹配
			   continue; 
		  }
		  cout<<"------------开始检索文件"<<Fullname<<" --------------"<<endl;
		
		  //就带全路径的文件名称转化为C风格的字符串,传给RegFile去统计
		  RegFile(Fullname.c_str());
	  }

	//所有的文件都检索完毕之后就开始对词频信息进行排序
	 SortInto();
	  FindClose(FileHandle );

}


BOOL comp(const Word *lhs ,const Word *rhs )  
{  
	return lhs->rate < rhs->rate;  
}  



void sortBySTL()
{
    Word * pWord;
    pWord=&MyWord[MAXCI];
   // std::sort(&MyWord,pWord,comp);
}

//根据出现的频率的排序
void SortInto() 
{
	DeleteFile("c://test//output.ini");
	int i=0,j;
	Word TmpWord;
	char rate_str[10];
	cout<<"开始排序,并输出到文件"<<endl;

	//根据结构体数组的频率进行冒泡排序
	for(i = 0;i <= MAXCI  ; i++ )
	{
		//如果词频为零就不进行排序也不写入
		if( MyWord[i].rate > 0)	
		{

			for(j = i+1 ; j < MAXCI ; j++ )
			{
				if(MyWord[i].rate < MyWord[j].rate)
				{
					TmpWord=MyWord[i];
					MyWord[i]=MyWord[j];
					MyWord[j]=TmpWord;
				}
			}

			//清空词频字符串缓冲区
			::memset(rate_str,0,10);

			//词频是整型,转化为字符串型
			itoa(MyWord[i].rate,rate_str,10);
			
			//讲词频信息写入到文件,格式:你好=25
			::WritePrivateProfileString("MyWord",MyWord[i].word.c_str(),rate_str,"c://ciku//output.ini");

			cout<<"“"<<MyWord[i].word<<"” 频率 = "<<MyWord[i].rate<<" —>用时(秒) ="<<(GetTickCount()-::starttime)/1000<<endl;
			
		}	
	}



}

//根据传入的文件名称进行匹配
void  RegFile(const char * filename)	
{

	char container[100];
	int index=0;
	char index_str[10];

	//正则表达式的匹配字串,就是从词库里面读取的词汇,然后去检索,类似一个模版
	regex expression;
	
	//用来接收匹配的结果
	smatch what;

	//当前要检索的文件
	ifstream myfile;

	//指向要匹配的文本的迭代器,it指向开始,end指向尾部
	std::string::const_iterator it;
	std::string::const_iterator end;

	//in要检索的文本内容
	std::string  in;
	std::string Tempstr;

	myfile.open(filename,ios::in);

	//如果打开失败就退出
	if(myfile.fail())
	{
		cout<<"open file "<<filename<<" failed"<<endl;
		return;
	}


	//从文件中读取一行
	while(getline(myfile,in))
	{
		//每次开始都将要匹配的字符串空间清空;
		in="";
		//从文件当前处按行读取,直到读取刚刚超过MAXLEN个字节,这里是20k
		while(in.length() < MAXLEN && getline(myfile,Tempstr) )
		{
			in.append(Tempstr);
			Tempstr="";	
		}

		//每次的比较都是从第一个词汇开始,第二轮的20K文件又要从第一个开始匹配
		index=0;	

		while(index<=MAXCI)
		{	
			//初始化迭代器
			it = in.begin();		
			end = in.end();

			//如果词库里面所有的信息都已经读到了内存 就不用读取
			if(ReadFlag &&  index<=MAXCI)	
			{

					::memset(container,0,100);
					itoa(index,index_str,10);
					//从 ciku.ini这个文件里依次读取词汇到container
					::GetPrivateProfileString("MyWord",index_str,"Nothing",container,99,"c://ciku//ciku.ini");

					//将
					MyWord[index].word.assign(container);
					MyWord[index].rate=0;

					//第一次从词库文件里面读取到MAXCI=32280个词汇
					if(index == MAXCI )
						//以后不用从文件里面读取了,所有词汇都已经在存到结构体里面了	
						ReadFlag=false;	
			}		

			//将当前的词汇作为一个正则表达式匹配串去检索相同的字符串
			expression.assign( MyWord[index].word.c_str() );

			//开始用上面的表达式来检索迭代器it和end所包含的内容,
			while(regex_search(it,end,what,expression))
			{	
				//每一次匹配到一个结果,就会返回指向匹配内容之后的iterator,
				//又将它付给it,这样就形成了一个滚动匹配
				it=what[0].second;	

				//匹配到一个词汇就将它对应的词频加一
				MyWord[index].rate++;

				//同时每一次匹配到一个词汇就将它删除,减少以后匹配的次数
			 	in.erase(in.find(MyWord[index].word.data()),MyWord[index].word.length());	
			}
			
			//如果词频不为零 打印一次匹配次信息
			if(MyWord[index].rate)	
			{  
		cout<<"匹配文件:"<<filename<<"\n当前匹配“"<<MyWord[index].word<<"”  第"<<index<<"个词 —>用时(秒)="<<(GetTickCount()-::starttime)/1000<<endl;
			}
			 
			//如果当前匹配文段长度为零 就直接结束本轮匹配
			if(in.length ==0) 
			{
				break;
			}

			//指向下一个词
			index++;
		}
	}
	//关闭文件
	myfile.close();

}


 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值