作者:finallyliuyu (转载请注明出处)
最近打算将自己的工作平台由C#,python等迁移到C++。这是我的第一个C++工作程序吧。
IDE:VS2008
language: C++
library:boost(安装boost库,先要安装python安装方法见《boost库安装方法》)
tools:weka
C++程序完成的功能:从数据库中读出文章-》分词(调用ICTCLAS)-》特征词选择(DF法)->VSM模型建立->把文章写成weka数据格式arff文件(此处写成的是稀疏数据的储存格式。weka教程见《教程》)
首先给出构造停用词集合的代码:
/* 获取停用词表 */
/************************************************************************/
set<string>MakeStopSet()
{ set<string> stopwordsSet;
ifstream ifile("stopwords.txt");
while(!ifile.eof())
{
string temp;
trim(temp," ");
ifile>>temp;
stopwordsSet.insert(temp);
}
return stopwordsSet;
}
然后我们给出调用ICTclas进行分词的代码,注意:工程中调用ICTCLAS时要把data 文件夹,config文件,ictclas30.h ICTCLAS30.dll,ICTCLAS30.LIB放在工程所在的文件夹。将ictclas30.h加入工程,在调用ICTCLAS30.DLL的cpp文件的头部加上#pragma comment(lib, "ICTCLAS30.lib")
/* c字符创形式的输入,string格式的输出,此函数用于调用ICTCLAS完成分词功能
/*
/************************************************************************/
string ICTsplit(const char *sInput)
{
if(!ICTCLAS_Init())
{
printf("ICTCLAS INIT FAILED!\n");
string strerr(sInput);
return strerr;
}
ICTCLAS_SetPOSmap(ICT_POS_MAP_SECOND);
//导入用户词典后
/*printf("\n导入用户词典后:\n");
int nCount = ICTCLAS_ImportUserDict("userdic.txt");//覆盖以前的用户词典
//保存用户词典
ICTCLAS_SaveTheUsrDic();
printf("导入%d个用户词。\n", nCount);*/
const char* sResult = ICTCLAS_ParagraphProcess(sInput, 0);
string strresult(sResult);
//printf("%s\n", sResult);
//把字符串转化成宽字符串
wstring wsResult=myMultibyteToWideChar(strresult);
boost::wregex wreg(L"\\s+");
wsResult=boost::regex_replace(wsResult,wreg,wstring(L"|"));
strresult=myWideCharToMultibyte(wsResult);
//ofile<<str1;
//ofile.close();
//cout<<str1<<endl;
//ICTCLAS_FileProcess("text.txt","test_result.txt",1);
ICTCLAS_Exit();
return strresult;
}
ICTclas分词结果默认的分割符是空格,在以上函数中,我们改成了“|”作为分隔符,字符串替换考率用boost的正则表达式库。因为我们要处理的是汉字字符串,所有要进行宽字符串窄字符串之间的转化,我采用的是利用win32函数的方法更多方法请见《boost正则表达式处理汉字字符串》。
/* 功能:将窄字符转化成宽字符,string->wstring */
/************************************************************************/
wstring myMultibyteToWideChar(string sResult)
{
int iWLen=MultiByteToWideChar( CP_ACP, 0, sResult.c_str(), sResult.size(), 0, 0 );// 计算转换后宽字符串的长度。(不包含字符串结束符)
wchar_t *lpwsz= new wchar_t [iWLen+1];
MultiByteToWideChar( CP_ACP, 0, sResult.c_str(), sResult.size(), lpwsz, iWLen ); // 正式转换。
lpwsz[iWLen] = L'\0';
wstring wsResult(lpwsz);
delete []lpwsz;
return wsResult;
}
/************************************************************************/
/* 将宽字符串转化成窄字符串用于输出 */
/************************************************************************/
string myWideCharToMultibyte(wstring wsResult)
{ string sResult;
int iLen= WideCharToMultiByte( CP_ACP, NULL, wsResult.c_str(), -1, NULL, 0, NULL, FALSE ); // 计算转换后字符串的长度。(包含字符串结束符)
char *lpsz= new char[iLen];
WideCharToMultiByte( CP_OEMCP, NULL, wsResult.c_str(), -1, lpsz, iLen, NULL, FALSE); // 正式转换。
sResult.assign( lpsz, iLen-1 ); // 对string对象进行赋值。
delete []lpsz;
return sResult;
}
有了以上的功能,我们现在编写一个函数,函数的输入是一篇文章,输出是一个词的集合。该词集合保存的是初步去掉噪声词后的“好词”
代码如下
/* 返回一篇文章中的好词 */
/************************************************************************/
vector<string>goodWordsinPieceArticle(string rawtext,set<string> stopwords)
{
vector<wstring> goodWordstemp;
vector<string> goodWords;
const char* sInput=rawtext.c_str();
string sResult=ICTsplit(sInput);
wstring wsResult=myMultibyteToWideChar(sResult);
boost::wregex wreg(L"\\d+");//去掉中文空格
wsResult=boost::regex_replace(wsResult,wreg,wstring(L""));
//boost::regex_split(back_inserter(goodWordstemp),wsResult,wreg);
boost::split(goodWordstemp,wsResult,boost::is_any_of("|"));
for(vector<wstring>::iterator it=goodWordstemp.begin();it!=goodWordstemp.end();it++)
{
string temp=myWideCharToMultibyte(*it);
trim(temp," ");
if(!stopwords.count(temp)&&!temp.empty())
{
goodWords.push_back(temp);
}
}
return goodWords;
}
上面的这个函数可以说是我们建立词袋子模型的基本单元,给上面的函数输入文章内容(rawtext),以及停用词表,那么它将返回一个词集合。下面我们开始构造词袋子模型。在构造词袋子模型之前,我们要说一下,我们词袋子模型的格式map<string,vector<pair<int,int>>>:主键为该词,pair中的第一个int 为文章标号,第二个词为在该文中出现的次数,vector<pair<int,int>>统计的是这个词在那些文章中出现,出现过几次。因为数据量比较大所以词袋子模型map,采用引用传参,如果是值传参的话,会在内存中产生拷贝,浪费内存
下面是从数据库中读文章建立词袋子模型的代码
int ConstructMap(map<string,vector<pair<int,int>>>&mymap,int beginindex,int endindex)
{
// vector<string> mySplit(string s);
set<string>MakeStopSet();
vector<string>goodWordsinPieceArticle(string rawtext,set<string>stopwords);
CoInitialize(NULL);
_ConnectionPtr pConn(__uuidof(Connection));
_RecordsetPtr pRst(__uuidof(Recordset));
char * select =new char[5000];
memset(select,0,5000);
char *firstpart="select CKeyWord,ArticleId,CAbstract from Article where ArticleId between ";
char *lastpart=" order by ArticleId";
char middlepart1[100];
char middlepart2[100];
sprintf_s(middlepart1,sizeof(middlepart1),"%d",beginindex);
sprintf_s(middlepart2,sizeof(middlepart2),"%d",endindex);
strcat(select,firstpart);
strcat(select,middlepart1);
strcat(select," and ");
strcat(select,middlepart2);
strcat(select,lastpart);
pConn->ConnectionString="Provider=SQLOLEDB.1;Password=xxxxxx;Persist Security Info=True; User ID=sa;Initial Catalog=ArticleCollection";
pConn->Open("","","",adConnectUnspecified);
pRst=pConn->Execute(select,NULL,adCmdText);
set<string>stopwords=MakeStopSet();
while(!pRst->rsEOF)
{ vector<string>wordcollection;
//string keywordstr=(_bstr_t)pRst->GetCollect("CKeyWord");
string rawtext=(_bstr_t)pRst->GetCollect("CAbstract");
if(rawtext!="")
{
wordcollection=goodWordsinPieceArticle(rawtext,stopwords);
string tempid=(_bstr_t)pRst->GetCollect("ArticleId");
int articleid=atoi(tempid.c_str());
for(vector<string>::iterator strit=wordcollection.begin();strit!=wordcollection.end();strit++)
{
vector<pair<int,int>>::iterator it;
if(mymap[*strit].empty())
{
pair<int,int>mytemppair=make_pair(articleid,1);
mymap[*strit].push_back(mytemppair);
}
else
{
for(it=mymap[*strit].begin();it!=mymap[*strit].end();it++)
{
if(it->first==articleid)
{
it->second=++(it->second);
break;
}
}
if(it==mymap[*strit].end())
{
pair<int,int>mytemppair=make_pair(articleid,1);
mymap[*strit].push_back(mytemppair);
}
}
}
}
pRst->MoveNext();
wordcollection.clear();
}
pRst->Close();
pConn->Close();
pRst.Release();
pConn.Release();
CoUninitialize();
delete[] select;
return 0;
}
未完,待续。。。。。