gbk编码的简介以及针对gbk文本飘红截断原理以及实现
2019獨角獸企業重金招聘Python工程師標準>>>
一個檢索系統,在歸并拉鏈并獲得摘要數據之后,必不可少的環節是飄紅截斷。
對于整個架構來說,檢索索引以及rank等后端一般使用c/c++來實現,真正的展現ui可以使用php/python等腳本語言來實現。對于飄紅而言,可以放在ui端使用php截斷飄紅,也可以放在后端通過c/c++來飄紅截斷。文本編碼可以用gbk也可以用utf-8。對于存儲而言,如果使用gbk編碼可以比utf-8
使用php的優點:操作簡單,有大量現成的庫,不需要關注gbk等具體實現,通過mb_str庫可以搞定所有事情,缺點在文本較長的時候性能及其低下。顯而易見,使用C/C++的有點是性能極高,但是確定是需要自己去關注字符編碼。
因為排期較緊,我起初使用了php來進行飄紅截斷,一個文本(字數大概在5k左右)飄紅大概花了200+ms,這個性能是不能忍受的,之后我只好改成了c/c++來進行飄紅截斷,性能得到顯著提升,耗時只有0.01ms。性能提升達到萬倍。
現在描述下怎么來使用c/c++來飄紅截斷文本。
1、 gbk簡介
首先先簡要介紹一下gbk(gb2312編碼)。
GBK是在國家標準GB2312基礎上擴容后兼容GB2312的標準,包含所有的中文字符。一個中文需要3個字節,最高位為1,所以第一個字節大于0x80. 此外字符編碼還有utf-8。gbk和utf-8之間可以通過Unicode編碼進行轉換。gbk,utf-8,Unicode之間的關系如果有不了解的請自己Google或者百度.
2、飄紅需求
2.1、輸入:
需要截斷文本content
需要飄紅的詞組hi_words,用"|"進行進行分割
需要截斷的字數 len
2.2、飄紅截斷規則
優先截斷字節數目一定;優先截取飄紅詞右邊的整句;如果整句不到截斷字數,在拿飄紅詞左邊的句子進行填充。
3、實現
3.1 把輸入的飄紅詞語翻入vector.
這個使用strtok_r輕輕松松搞定(注意不要使用非線程安全的strtok).
int?getHighWord(char*?high_words,?vector<string>&?words) {char?keyword[1024];char?*ptok?=?NULL;snprintf(keyword,?sizeof(keyword),?"%s",?high_words);char?*part?=?strtok_r(keyword,?"|",?&ptok);while(?part?!=?NULL){string?tmp(part);words.push_back(tmp);part?=?strtok_r(NULL,?"|",?&ptok);}return?0; }? ? 3.2、把句子分隔符放入到vector中。
sep_china是中文句子分隔符,sep_uni是英文分割符,他們都是一個整句。
vector<string>?sep_china; vector<string>?sep_uni; sep_china.push_back(","); sep_china.push_back("。"); sep_china.push_back(";"); sep_china.push_back(":"); sep_china.push_back("!");sep_uni.push_back(","); sep_uni.push_back("."); sep_uni.push_back("?"); sep_uni.push_back(";"); sep_uni.push_back(":");3.3、具體處理流程
3.4?判斷有沒有這些words,返回pos_word
string?str_cnt(content);//首先判斷有沒有這個wordint?pos_word?=?-1;string?min_word;int?i;for(i=0;?i<words.size();?i++){pos_word?=?str_cnt.find(words[i]);if(pos_word?>?0){min_word?=?words[i];break;}}//如果沒有找到,直接截斷返回if(?pos_word?<?0){if(?num?<=?getExtraWord(content,?hi_word,?0,?num)?){strcat(hi_word,"...");return?0;}}3.5?計算word前面字符數before,后面字符串after,還需要截斷字符數left,以及word前面第一個標點的偏移位置quoto(需要考慮中文和英文)。
//還需要多少字節int?left?=?num?-?min_word.size();//前面有多少字節int?before?=?pos_word;//后面還有多少字節int?after?=?str_cnt.size()?-?min_word.size()?-?pos_word;//獲得前一個標點的位置int?pos_quoto_china?=?-1;int?pos_quoto_uni?=?-1;string?quoto_str?=?str_cnt.substr(0,?pos_word);pos_quoto_china?=?findMaxPos(quoto_str,?sep_china)?+?2;?//一個標點2個字符?pos_quoto_uni?=?findMaxPos(quoto_str,sep_uni)?+1?;?//一個標點一個字符int?quoto_pos?=?pos_quoto_uni?>?pos_quoto_china???pos_quoto_uni?:?pos_quoto_china;//獲得前一個標點有多少字節int?quoto?=?quoto_pos?>?0???pos_word?-?quoto_pos?:?0;3.6?根據before, after, quoto, left計算目前status
int?getStatus(int?before,?int?after,?int?quoto,?int?left) {int?status?=?0;if(quoto?>=?left){//直接截斷quotostatus?=?0;}else?if(?quoto?+?after?>?left){//返回截斷quoto+leftstatus?=1?;}else{//返回截斷after和left-after的前面status?=2;}return?status; }3.7?根據返回的status進行處理
char?before_word[1024];char?after_word[1024];int?left_cnt;int?right_cnt;before_word[0]?=?after_word[0]?=?'0';switch(status){case?0:getExtraWord(content,?hi_word,?pos_word,?left);strcat(hi_word,?"...");break;case?1:left_cnt?=?getExtraWord(content,?before_word,?pos_word?-1,?-1?*?quoto);right_cnt?=?getExtraWord(content,?after_word,?pos_word?+?min_word.size(),?left-?quoto);if(?left_cnt?<?pos_word){strcpy(hi_word,?"...");}strcat(hi_word,?before_word);strcat(hi_word,?min_word.c_str()?);strcat(hi_word,?after_word);strcat(hi_word,?"...");break;case?2:right_cnt?=?getExtraWord(content,?before_word,?pos_word?+?min_word.size()?,?after);left_cnt?=?getExtraWord(content,?after_word,?pos_word-1?,?after-?left);if(?left_cnt?<?pos_word){strcpy(hi_word,?"...");}strcat(hi_word,?before_word);strcat(hi_word,?min_word.c_str()?);strcat(hi_word,?after_word);strcat(hi_word,?"...");break;default:getExtraWord(content,?hi_word,?0,?num);break;}3.8?截斷函數
其中最為核心的為截斷函數,如下所示,從word的begin位置截取length個字符(length小于0表示從左截斷,0x80是判斷標準。
int?getExtraWord(const?char*?word,?char*?res,?int?begin,?int?length) {if(length?==?0?){res[0]?=?'\0';return?0;}int?i;int?flag;const?char?*str?=?word?+?begin;const?char?*chech_vaild?=?(length?>0?)??str?:?str+1;if(?false?==?checkVaild(chech_vaild)?){cout?<<?"not?vaild\n";//盡力修復下吧begin?++;str++;}//如果從后截斷?word沒有?length,直接返回if(?(int)strlen(str)??<=?length??){strcpy(res,?str);return?strlen(str);}//如果從前截斷?word沒有lenght長if(?begin?<=?-1?*?length)?{strncpy(res,?word,?begin);res[begin?]?=??'\0';return?begin;}flag?=?length?>?0???1?:?-1;int?num?=?0;while(?*str?){//如果是中文if((unsigned?char)*str?>?0x80){num?+=?2;str?+=?flag?*?2;}else{num?++;str?+=?flag;}if(?num?>=?flag?*?length){break;}}res[0]?=?'\0';i?=?0;str?=?(length??>?0)???word?+?begin?:?word?+?begin?-?num?+?1;while(*str){res[i++]?=??*(str++);if(i==num){break;}}res[i]?=?'\0';return?i; }4、結果
4.1 測試case:
int?main() {const?char*?content?=?"啊。雞?從到信陽,然后在火車站有直達雞公山的汽車,只收10元。約一個小時就到雞公山腳下,以前票價是63,自從港中旅進駐雞公山后,現在好像改成123了,當然了,港中旅把雞公山搞得更加漂亮和特色了。?在雞公山門口有去山頂的旅游大巴,單程15元,往返的20元,不過建議大家做單程上去,然后步行從登山古道或者長生谷下山,會更加有趣味。和朋友在登山古道的入口處景區處于全監控狀態,所以相當安全。美齡舞廳外面的招牌很顯眼啊。頤廬是雞公山上最有名的建筑,盡管世界各國都曾有在山上建房子,但惟獨中國人靳";vector<string>?words;char*?hi?=?"信陽";getHighWord(hi,?words);for(int?i=0;?i<words.size();?i++){cout?<<?words[i]?<<?endl;}char?hi_word[1024];hi_word[0]?=?'\0';getHilight(content,?hi_word,?words,?200);cout?<<?"hi_word?is?"?<<?hi_word?<<?endl; }4.2?結果:
5、全部源代碼
#include?<stdio.h> #include?<iostream> #include?<string> #include?<vector> using?namespace?std;/**?查看一句話是否是完整的gbk語句?*/ bool?checkVaild(const?char*?word) {bool?vaild?=?true;while(*word){if(?(unsigned?char)*word++?>?0x80?){vaild?=?!vaild;}}return?vaild; }int?getExtraWord(const?char*?word,?char*?res,?int?begin,?int?length) {if(length?==?0?){res[0]?=?'\0';return?0;}int?i;int?flag;const?char?*str?=?word?+?begin;const?char?*chech_vaild?=?(length?>0?)??str?:?str+1;if(?false?==?checkVaild(chech_vaild)?){cout?<<?"not?vaild\n";//盡力修復下吧begin?++;str++;}//如果從后截斷?word沒有?length,直接返回if(?(int)strlen(str)??<=?length??){strcpy(res,?str);return?strlen(str);}//如果從前截斷?word沒有lenght長if(?begin?<=?-1?*?length)?{strncpy(res,?word,?begin);res[begin?]?=??'\0';return?begin;}flag?=?length?>?0???1?:?-1;int?num?=?0;while(?*str?){//如果是中文if((unsigned?char)*str?>?0x80){num?+=?2;str?+=?flag?*?2;}else{num?++;str?+=?flag;}if(?num?>=?flag?*?length){break;}}res[0]?=?'\0';i?=?0;str?=?(length??>?0)???word?+?begin?:?word?+?begin?-?num?+?1;while(*str){res[i++]?=??*(str++);if(i==num){break;}}res[i]?=?'\0';return?i; }int?getStatus(int?before,?int?after,?int?quoto,?int?left) {int?status?=?0;if(quoto?>=?left){//直接截斷quotostatus?=?0;}else?if(?quoto?+?after?>?left){//返回截斷quoto+leftstatus?=1?;}else{//返回截斷after和left-after的前面status?=2;}return?status; }int?findMaxPos(string?content,?vector<string>quotos) {int?i;int?pos?=?string::npos;int?tmp_pos;for(i=0;?i<quotos.size();?i++){tmp_pos?=?content.rfind(quotos[i]);if(?tmp_pos?>?pos){pos?=?tmp_pos;}}return?pos; }int?getHilight(const?char*?content,?char*?hi_word,??vector<string>?words,?int?num) {//如果不夠長if(?strlen(content)?<=?num?){strcpy(hi_word,?content);return?0;}//首先分詞vector<string>?sep_china;vector<string>?sep_uni;sep_china.push_back(",");sep_china.push_back("。");sep_china.push_back("?");sep_china.push_back(";");sep_china.push_back(":");sep_china.push_back("!");sep_uni.push_back(",");sep_uni.push_back(".");sep_uni.push_back("?");sep_uni.push_back(";");sep_uni.push_back(":");sep_uni.push_back(";");//sep_uni.push_back("?");//內容string?str_cnt(content);//首先判斷有沒有這個wordint?pos_word?=?-1;string?min_word;int?i;for(i=0;?i<words.size();?i++){pos_word?=?str_cnt.find(words[i]);if(pos_word?>?0){min_word?=?words[i];break;}}//如果沒有找到,直接截斷返回if(?pos_word?<?0){if(?num?<=?getExtraWord(content,?hi_word,?0,?num)?){strcat(hi_word,"...");return?0;}}//還需要多少字節int?left?=?num?-?min_word.size();//前面有多少字節int?before?=?pos_word;//后面還有多少字節int?after?=?str_cnt.size()?-?min_word.size()?-?pos_word;//獲得前一個標點的位置int?pos_quoto_china?=?-1;int?pos_quoto_uni?=?-1;string?quoto_str?=?str_cnt.substr(0,?pos_word);pos_quoto_china?=?findMaxPos(quoto_str,?sep_china)?+?2;?//一個標點2個字符?pos_quoto_uni?=?findMaxPos(quoto_str,sep_uni)?+1?;?//一個標點一個字符int?quoto_pos?=?pos_quoto_uni?>?pos_quoto_china???pos_quoto_uni?:?pos_quoto_china;//獲得前一個標點有多少字節int?quoto?=?quoto_pos?>?0???pos_word?-?quoto_pos?:?0;int?status?=?getStatus(before,?after,?quoto,?left);char?before_word[1024];char?after_word[1024];int?left_cnt;int?right_cnt;before_word[0]?=?after_word[0]?=?'0';switch(status){case?0:getExtraWord(content,?hi_word,?pos_word,?left);strcat(hi_word,?"...");break;case?1:left_cnt?=?getExtraWord(content,?before_word,?pos_word?-1,?-1?*?quoto);right_cnt?=?getExtraWord(content,?after_word,?pos_word?+?min_word.size(),?left-?quoto);if(?left_cnt?<?pos_word){strcpy(hi_word,?"...");}strcat(hi_word,?before_word);strcat(hi_word,?min_word.c_str()?);strcat(hi_word,?after_word);strcat(hi_word,?"...");break;case?2:right_cnt?=?getExtraWord(content,?before_word,?pos_word?+?min_word.size()?,?after);left_cnt?=?getExtraWord(content,?after_word,?pos_word-1?,?after-?left);if(?left_cnt?<?pos_word){strcpy(hi_word,?"...");}strcat(hi_word,?before_word);strcat(hi_word,?min_word.c_str()?);strcat(hi_word,?after_word);strcat(hi_word,?"...");break;default:getExtraWord(content,?hi_word,?0,?num);break;}return?0; }int?getHighWord(char*?high_words,?vector<string>&?words) {char?keyword[1024];char?*ptok?=?NULL;snprintf(keyword,?sizeof(keyword),?"%s",?high_words);char?*part?=?strtok_r(keyword,?"|",?&ptok);while(?part?!=?NULL){string?tmp(part);words.push_back(tmp);part?=?strtok_r(NULL,?"|",?&ptok);}return?0; }int?main() {const?char*?content?=?"啊。雞?從到信陽,然后在火車站有直達雞公山的汽車,只收10元。約一個小時就到雞公山腳下,以前票價是63,自從港中旅進駐雞公山后,現在好像改成123了,當然了,港中旅把雞公山搞得更加漂亮和特色了。?在雞公山門口有去山頂的旅游大巴,單程15元,往返的20元,不過建議大家做單程上去,然后步行從登山古道或者長生谷下山,會更加有趣味。和朋友在登山古道的入口處景區處于全監控狀態,所以相當安全。美齡舞廳外面的招牌很顯眼啊。頤廬是雞公山上最有名的建筑,盡管世界各國都曾有在山上建房子,但惟獨中國人靳";vector<string>?words;char*?hi?=?"信陽";getHighWord(hi,?words);for(int?i=0;?i<words.size();?i++){cout?<<?words[i]?<<?endl;}char?hi_word[1024];hi_word[0]?=?'\0';getHilight(content,?hi_word,?words,?100);cout?<<?"hi_word?is?"?<<?hi_word?<<?endl; }轉載于:https://my.oschina.net/jungleliu0923/blog/198469
總結
以上是生活随笔為你收集整理的gbk编码的简介以及针对gbk文本飘红截断原理以及实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【手机游戏开发优化篇】详解手游[体积]及
- 下一篇: java cpu 占用高问题定位