前面碰到的正則表達(dá)式匹配時,產(chǎn)生的內(nèi)存耗盡異常,大概提到兩種方案,一種是給匹配建立線程,超時時把它殺死。另一種則從其根源上看,即正則表達(dá)式產(chǎn)生內(nèi)存耗盡異常的原因是,由于匹配是個np完全問題,為了避免無限執(zhí)行下去,所以實(shí)現(xiàn)中一般對匹配中達(dá)到的狀態(tài)進(jìn)行了計數(shù),當(dāng)狀態(tài)達(dá)到了一定數(shù)目則拋出這個異常。
究其原因是因?yàn)槠ヅ浔磉_(dá)式中出現(xiàn)了太多的.*,導(dǎo)致匹配的可能性大大增加,出現(xiàn)的回溯次數(shù)增多,對于web頁面的匹配,我們結(jié)合一下,提出如下匹配方式,即把原來的一次匹配,改成多層匹配,即首先利用上層匹配,縮小匹配的文本,然后再在已經(jīng)滿足上層匹配的文本中進(jìn)行下層的匹配。
比如(?<=<li class=g>).*?<.*?><a.*?href="(.*?)"[^>]*?>(.*?)</a>(.*?)<br>.*?<cite>(.*?)</CITE>.*?(?=</a></span>)
我們則拆成下面兩個匹配過程:
(?<=<li class=g>)(.*?)(?=</a></span>)
.*?<.*?><a.*?href="(.*?)"[^>]*?>(.*?)</a>(.*?)<br>.*?<cite>(.*?)</CITE>.*?
具體實(shí)現(xiàn),我們則將原來的正則表達(dá)式,改成多個的組合,兩個正則式之間用token char隔離。
注意:
1.由于我們采用了空格作為分割正則表達(dá)式的標(biāo)志,因此必須把正則表達(dá)式本身含有的空格全部使用\s替代。因此必須注意保證config.xml里的正則表達(dá)式與這里所采用的處理方式相一致。
2.正則表達(dá)式中,不要出現(xiàn)不成對的<,因?yàn)楹竺嫖覀円?lt;>去除標(biāo)簽內(nèi)的文字。
3.有時候雖然在正則匹配測試工具里匹配成功,但是在程序中也可能依然錯誤,原因就是,在我們程序中對正則式中空格的特殊性的使用造成的。
現(xiàn)在的實(shí)現(xiàn)實(shí)際上是遞歸實(shí)現(xiàn):實(shí)際上我們在遞歸時采用的是what[0].也就意味著實(shí)際上我們用上層匹配的結(jié)果作為下層的范圍。實(shí)際上如果我們修改成what[1]將允許我們使用更加復(fù)雜的篩選.比如我們(?<=<li
recurseSearch(regex_exprs,web_text,set,what[0].first,what[0].second,level+1);
/*
完成正則表達(dá)式的多層匹配,上一層的匹配結(jié)果作為下一層的輸入串,繼續(xù)進(jìn)行匹配,層層過濾。
參數(shù)如下:
regex_exprs-代表所有各層正則表達(dá)式的集合
web_text-要處理的網(wǎng)頁文本
set-保存結(jié)果的集合
start-待處理的文本的起始處
end- 待處理的文本的結(jié)束處
level-控制結(jié)束,及當(dāng)前處理那個正則表達(dá)式的層數(shù),從1開始計數(shù)。
*/
void HtmlParser::recurseSearch(vector<boost::regex> ®ex_exprs,WebText &web_text,ResultSet &set,str_const_iterator start,str_const_iterator end,int level){
//int index = web_text.start_index;
boost::smatch what;
boost::regex expression = regex_exprs[level-1];
while( boost::regex_search(start, end, what, expression) )
{
//如果是最終的那個正則表達(dá)式 ,則開始搞結(jié)果
if(level == regex_exprs.size()){
Result elem(web_text.source.getName());
int sub_count = (int)what.size();
for(int i = 1 ; i < sub_count ; i++ ){
std::string msg(what[i].first, what[i].second);
elem.setBySubNumber(msg,(RES_CONTENT_TYPE)i);
}
//注意我們這里修改了 web_text.start_index的值,用它來給結(jié)果在網(wǎng)頁中的索引賦值。
elem.setIndexInSource(web_text.start_index);
web_text.start_index++;
elem.deleteBrace();
set.addResult(elem);
}
else{
recurseSearch(regex_exprs,web_text,set,what[0].first,what[0].second,level+1);
}
start = what[0].second;
}
}
/*
利用正則表達(dá)式,從網(wǎng)頁中抽取每條搜索結(jié)果,具體則通過調(diào)用 HtmlParser::recurseSearch實(shí)現(xiàn)
并將結(jié)果直接保存到 ResultSet中
使用時,由 ResultSet調(diào)用,同時本身回調(diào)ResultSet里的addResult方法,添加搜索結(jié)果
*/
void HtmlParser::webParse(WebText &web_text,ResultSet &set){
try{
debug_log("HtmlParser::webParse:download web:" ,web_text.text);
string web_regex = web_text.getRegex();
vector<boost::regex> regex_exprs;
/*
注意,如果選擇'!',則只能處理一個正則式的情況,無法處理多層正則的情況。
所以如果想恢復(fù)原來的模式,只需要把這里的string_token()的第二個參數(shù)改成'!',
同時保證config.xml里的正則表達(dá)式只有一個即可。
*/
vector<string> regex_tokens = string_token(web_regex,' ');
for(int i = 0 ; i < regex_tokens.size(); i++){
boost::regex expression(regex_tokens[i],boost::regex::perl|boost::regex::icase);
regex_exprs.push_back(expression);
}
str_const_iterator start = web_text.text.begin();
str_const_iterator end = web_text.text.end();
recurseSearch(regex_exprs,web_text,set,start,end,1);
}
catch(boost::regex_error e){
string exception_str = string("exception:expression is not a valid regular expression: \"") + e.what() + "\"";
debug_log(web_text.getSiteName()+"-HtmlParser::webParse:",exception_str);
cout<< exception_str <<endl;
}
catch(std::runtime_error e){
string exception_str = string("exception:expression runtime_error: \"") + e.what() + "\"";
debug_log(web_text.getSiteName()+"HtmlParser::webParse:",exception_str);
cout<< exception_str <<endl;
}
catch(std::bad_alloc e){
string exception_str = string("exception:expression bad_alloc: \"") + e.what() + "\"";
debug_log(web_text.getSiteName()+"HtmlParser::webParse:",exception_str);
cout<< exception_str <<endl;
}
}
以前的實(shí)現(xiàn)如下:
/*
void HtmlParser::webParse(WebText &web_text,ResultSet &set){
try{
string web_regex = web_text.getRegex();
boost::regex expression(web_regex,boost::regex::perl|boost::regex::icase);
boost::smatch what;
str_const_iterator start = web_text.text.begin();
str_const_iterator end = web_text.text.end();
debug_log("HtmlParser::webParse:download web:" ,web_text.text);
int index = web_text.start_index;
while( boost::regex_search(start, end, what, expression) )
{
Result elem(web_text.source.getName());
//std::cout<< "Have:" ;
int sub_count = (int)what.size();
for(int i = 1 ; i < sub_count ; i++ ){
std::string msg(what[i].first, what[i].second);
//std::cout<< msg.c_str() << std::endl;
elem.setBySubNumber(msg,(RES_CONTENT_TYPE)i);
}
start = what[0].second;
elem.setIndexInSource(index);
index++;
elem.deleteBrace();
set.addResult(elem);
}
}
catch(boost::regex_error e){
string exception_str = string("exception:expression is not a valid regular expression: \"") + e.what() + "\"";
debug_log(web_text.getSiteName()+"-HtmlParser::webParse:",exception_str);
cout<< exception_str <<endl;
}
catch(std::runtime_error e){
string exception_str = string("exception:expression runtime_error: \"") + e.what() + "\"";
debug_log(web_text.getSiteName()+"HtmlParser::webParse:",exception_str);
cout<< exception_str <<endl;
}
catch(std::bad_alloc e){
string exception_str = string("exception:expression bad_alloc: \"") + e.what() + "\"";
debug_log(web_text.getSiteName()+"HtmlParser::webParse:",exception_str);
cout<< exception_str <<endl;
}
}
*/