当前位置: 首页 > news >正文

职工素质建设 网站惠州网站外包

职工素质建设 网站,惠州网站外包,wordpress 慢 2019,专业精准网络营销推广BoostSearcher搜索引擎项目 1.BoostSearcher这个项目是什么#xff1f; 答#xff1a;一个为Boost文档建立索引的站内搜索引擎#xff0c;简单的说就是一个类似于csdn站内文档搜索框。 项目展示#xff1a; gitee:https://gitee.com/zxlfx/boost-search-engine-project …BoostSearcher搜索引擎项目 1.BoostSearcher这个项目是什么 答一个为Boost文档建立索引的站内搜索引擎简单的说就是一个类似于csdn站内文档搜索框。 项目展示 gitee:https://gitee.com/zxlfx/boost-search-engine-project 2.为什么做BoostSearcher 答学习和了解搜索引擎的底层原理虽然无法做出一个像百度、360搜索这样的全网搜索引擎但可以从中窥探出搜索引擎所具备的一些宏观设计。 3.怎么做BoostSearcher 答我分为以下几个步骤完成BoostSearcher项目 了解BoostSearcher宏观原理。获取数据源html网页对数据建立正排、倒排索引核心编写html服务为前端提供服务。日志的补充及项目的扩展。 4.项目设计的技术 前端HTML5、CSS、JS、jQuery、Ajax不需要了解太多该项目主要是后端编写不写前端也行 后端C/C、C11,STL库、标准库Boost主要文件库、jsoncpp序列化和反序列化、cppjieba分词工具、cpphttp-lib(提供http服务) 5.介绍宏观原理 宏观图 ​ 宏观图解释 做法是先获取数据源html网页如何获取 答从Boost官网下载文档HTML网页。 对HTML网页进行解析和提取如何解析和提取 答解析网页HTML提取出title、摘要、url。组成一个个结构体构成关键字的查找依据。 建立正排和倒排索引什么是正排索引什么是倒排索引 答正排索引如下文档id和正排文档的映射关系。 文档id正排文档DocInfo1正排文档12正排文档23正排文档34正排文档4倒排索引如下关键字和倒排拉链的映射关系 关键字倒排拉链(Inverted_List)我倒排文档1、倒排文档2、倒排文档3不吃倒排文档2香菜倒排文档1、倒排文档3struct DocInfo//正排文档 {int id;//文档id这个作用后续会解答。std::string title;//文档标题std::string content;//文档内容,这里的内容指的是全html文本去掉标签后的字符串如head你好head/去掉之后就是你好”std::string url;//文档url }struct Inverted_ele//倒排文档 {int id;//文档iddouble weight;//关键字在该文档的权重std::string word;//关键字这个作用后续会解答 }typedef std::vectorInverted_ele Inverted_List;//倒排拉链如果我们需要查找这样的一句话我必须全力守卫我的祖国 。 根据分词工具分解成以下关键字“我”、“必须”、“全”、“力”、“全力”、“守卫”、“我的祖国”、“我的”、“祖国”。 具体分解成什么样的关键字我们不需要关心这是分词工具做的事情本项目只使用分词工具不设计原理细节。 接下来只需要把这些关键字对应的倒排拉链Inverted_List全部组合到一起组合方式相同的倒排文档权重相加然后放到一个新的Inverted_List中。并进行权重降序排序然后就可以通过倒排文档(Inverted_ele)找到对应的正排文档(DocInfo)即找到一个个title、content、url然后将一个个正排文档序列化组成字符串返回给客户端。 注序列化将结构体数据转化为字符串。 6、项目编写 6.1 获取数据源 第一步从Boost官网下载文档网页集合然后用rz命令上传至服务器。 ​ ​ 我们只需要html中的.html文件其他的不需要。 ​ 将html目录下的所有文件拷贝到raw目录下。 提取raw下的所有HTML文件为一个个DocInfo结构体用\3分割title\3content\3url再用\n分割DocInfo结构体最后组成一个大文本文件。 struct DocInfo {std::srting title;std::string content;std::string url; }大文本文件内容简述title\3content\3url \n title\3content\3url \n …这里我用二进制写文件不用二进制写文件也行 编写解析代码parser.cc ***第一步搭建框架*** #includeiostream #includestring #includevector #includeboost/filesystem.hpp #includeutil.hppconst std::string src_path ./data/input;//源目录 const std::string raw_path ./data/raw/raw.txt;//解析源目录下的所有.html文件组成一个raw.txt大文本文件typedef struct DocInfo{std::string title;//标题std::string content;//内容std::string url;//url }DocInfo_t;int main() {std::vectorstd::string filenames;std::vectorDocInfo_t results;//将src_path目录下的所有.html普通文件全部列举到filenames容器中if(!EnumFile(src_path,filenames)){std::cerrEnumFile() errorstd::endl;return 1;} //读取一个个filenames中的带路径的文件名进行解析成一个个DocInfo_t结构体保存到results容器中if(!ParseHtml(filenames,results)){std::cerrParseHtml() errorstd::endl;return 2;}//将results容器中的一个个DocInfo结构体保存到raw_path中DocInfo的成员变量之间用\3分隔DocInfo用\n分割。//如itle\3content\3url\ntitle\3content\3url\n//为什么用\n当DocInfo结构体的分割符\3当DocInfo成员变量的分隔符//答为了之后读取方便后续我们可以用getline(cin,s)可以非常方便读取一个完整的DocInfo选择\3主要它是一个不可显字符html文本中一般不会含有该字符。//注意getline不读取\n到s中同时会去掉缓冲区中的\n。if(!SaveHtml(results,raw_path)){std::cerrSaveHtml() errorstd::endl;return 3;}return 0; }接下来只需要完成EnumFile()、ParseHtml()、SaveHtml()函数的编写 EnumFIle()函数 由于C的文件操作不适应递归遍历目录文件因此我选择使用Boost文件系统库。 安装boost库命令sudo yum install -y boost-devel 如何使用Boost文件系统库呢 这里只对用到的函数代码做解释具体学习还需要到Boost官网看文档学习。 bool EnumFile(const std::string src_path,std::vectorstd::string filenames) {namespace fs boost::filesystem;//引用命名空间。fs::path root_path(src_path);//定义path变量用src_path初始化。if(!fs::exists(root_path))//判断root_path目录是否存在即判断src_path目录是否存在。{std::cerrsrc_pathis not existsstd::endl;return false;}fs::recursive_directory_iterator end;//迭代器的结束位置。for(fs::recursive_directory_iterator iter(root_path);iter ! end;iter)//迭代器遍历。{if(!fs::is_regular_file(*iter))//判断是不是普通文件。{continue;//如果是目录则直接判断下一个。}if(iter-path().extension() ! .html)//判断普通文件的后缀是不是.html。{continue;//后缀不满足判断下一个。}//得到了一个带路径的html普通文件。filenames.push_back(iter-path().string());}return true; }​ 2.ParseHtml()函数 ​ 该函数的作用是读取一个个带路径的html文件然后解析提取为一个个DocInfo结构体保存到results容器中(vector)。 ParseHtml()函数基本框架 bool ParseHtml(const std::vectorstd::string filenames,std::vectorDocInfo_t results) {for(std::string file:filenames)//遍历读取带路径的html文件名{std::string result;//读取html文件内容到result中if(!ns_util::FileUtil::ReadFile(file,result))//由于可能多个地方需要读取文件于是将ReadFile写到了一个工具类{continue;}DocInfo_t doc;//提取html文件的内容到doc结构体中if(!ParseTitle(result,doc.title))//提取标题需要的是result。{continue;}if(!ParseContent(result,doc.content))//提取内容需要的是result。{continue;}if(!ParseUrl(file,doc.url))//提取url需要的是html文件路径名。{continue;}results.push_back(std::move(doc));//doc插入到results容器中使用move调用移动赋值减少拷贝。}return true; }util.hpp中ReadFIle()的编写 #pragma once #includestring #includeiostream #includefstream #includevector #includeboost/algorithm/string.hpp #includeunordered_mapnamespace ns_util {namespace FileUtil{static bool ReadFile(const std::string file,std::string* result){std::ifstream ifs(file,std::ios::in);if(!ifs.is_open()){std::cerrfile open errorstd::endl;return false;}std::string line;while(getline(ifs,line)){*result line;}return true;}} } 接下来依次完成ParseTitle()、ParseContent()、ParseUrl()编写。 ParseTitle()提取标题 static bool ParseTitle(const std::string result,std::string* title) {//比如解析title你是个好人/titlestd::size_t begin result.find(title);if(begin std::string::npos){return false;}begin std::string(title).size();std::size_t end result.find(/title);if(end std::string::npos){return false;}if(begin end){return false;}*title result.substr(begin,end-begin);return true; }ParseContent()提取内容 static bool ParseContent(const std::string result,std::string* content) {//简易的状态机读一读代码很好理解enum status{LABLE,CONTENT };status s LABLE;for(char c : result){switch(s){case LABLE:if(c )s CONTENT;break;case CONTENT:if(c )s LABLE;else {if(c \n)//这里我把html文本中的所有\n全部替换成了 就是为了用getline后面读取一个个DocInfo结构体方便所以需要替换掉\n选择 可以选择\4可以。但\3不行会和DocInfo成员变量的SEP分隔符冲突。c ;*content c;} break;default:break;}}return true; }ParseUrl()提取Url static bool ParseUrl(const std::string file,std::string* url) {std::string url_head https://www.boost.org/doc/libs/1_81_0/doc/html;//官方的文档访问路径std::string url_end file.substr(src_path.size());//因为file是带路径的文件名需要把它前面的本地路径去掉换上官网的路径。*url url_head url_end;return true;//注搜索引擎不存储站点网站只是将搜索结果的url返回告诉你该访问哪个网站。 }SaveHtml()函数保存results的内容为一个大文本文件。 bool SaveHtml(const std::vectorDocInfo_t results,const std::string raw_path) { #define SEP \3//DocInfo成员函数分隔符如title\3content\3urlstd::ofstream ofs(raw_path,std::ios::out | std::ios::binary);//二进制写打开文件if(!ofs.is_open()){std::cerrraw_pathopen errorstd::endl;return false;}for(auto item : results)//遍历results中的一个个DocInfo结构体{std::string out_string;out_string item.title;out_string SEP;out_string item.content;out_string SEP;out_string item.url;out_string \n;ofs.write(out_string.c_str(),out_string.size());//将out_string写入打开的文件}ofs.close();return true; }至此完成了一个文件的清洗最后得到了一个包含若干个DocInfo字符串的大文本文件。 6.2 建立正排和倒排索引 本项目终于迎来了它的核心部分正排索引和倒排索引 第一步建立正排索引 我们前面简述过了一遍正排索引的原理这里我再次分析一遍。 文档id文档DocInfo1文档12文档23文档34文档4 正排索引即文档id与文档DocInfo的映射关系这里的DocInfo结构体为 struct DocInfo//正排文档 {int id;//文档id这个作用后续会解答。std::string title;//文档标题std::string content;//文档内容,这里的内容指的是全html文本去掉标签后的字符串如head你好head/去掉之后就是你好”std::string url;//文档url }注意与parser.cc里面的DocInfo结构体区分这里多了一个id作用会后续解答。 倒排索引即关键字与倒排拉链的映射关系。 什么是关键字 如果一句话为”我不吃香菜“ 那么关键字可以为“我”、“不吃”、“香菜”(具体取决于分词工具) 关键字倒排拉链(Inverted_List)我倒排文档1、倒排文档2、倒排文档3不吃倒排文档2香菜倒排文档1、倒排文档3 倒排拉链是什么 struct Inverted_ele//倒排文档 {int id;//文档iddouble weight;//关键字在该文档的权重std::string word;//关键字这个作用后续会解答 }typedef std::vectorInverted_ele Inverted_List;//我称它为倒排拉链倒排拉链即 std::vectorInverted_ele 如果用户搜索“我不吃”那么分词工具大概率分为“我”、“不吃”。 然后查找倒排索引找到各个关键字对应的倒排拉链就是: 倒排文档1、倒排文档2、倒排文档3 倒排文档2 再合并成一个新的倒排拉链倒排文档1、倒排文档2、倒排文档3,相同的倒排文档权重需要相加因为倒排文档2中既有关键字“我”也有“不吃”权重需要相加。 然后通过倒排文档中的id查正排索引找到对应的正排文档正排文档1、正排文档2、正排文档3。 然后将各个正排文档由结构体转化为字符串组合成一个Json字符串返回由于内存对齐等平台原因网络上直接发送结构体二进制数据容易出错。这里需要用序列化与反序列化工具Json、Xml、Protobuf我选择的是Json。 说了这么多了现在开始完成索引的建立 索引框架的编写 #pragma once #includeiostream #includestring #includevector #includeunordered_map #includefstream #includeutil.hpp #includemutexnamespace ns_index {struct ForwardElem//正排文档{std::string title;//标题std::string content;//内容std::string url;//urluint64_t id;//正排文档id};struct InvertedElem//倒排文档{uint64_t id;//正排文档idint weight;//文档权重std::string word;//关键字};typedef std::vectorInvertedElem InvertedList;//倒排拉链class Index{private:std::vectorForwardElem forward_index;//正排索引std::unordered_mapstd::string,InvertedList inverted_index;//倒排索引public:Index(){};~Index(){};public:ForwardElem* GetForwardIndex(uint64_t id)//根据文档id获取正排文档函数1{}InvertedList* GetInveredList(std::string word)//根据关键字获取倒排拉链函数2{}bool BuildIndex(const std::string raw_path)//根据parser.cc得到的大文本文件建立正排和倒排索引函数3{}private:ForwardElem* BuildForwardIndex(const std::string line)//建立正排索引函数4{}bool BuildInvertedIndex(const ForwardElem fe)//建立倒排索引函数5{}}; }现在剩下的是我们需要完成对上面五个函数代码编写。 先来GetForwardIndex()函数:通过文档id获取正排文档 ForwardElem* GetForwardIndex(uint64_t id){if(id forward_index.size())//防止越界{std::cerrid index beyond rangestd::endl;return nullptr;}return forward_index[id];//这里需要用返回值来建立倒排索引后面会用到该返回值。}编写GetInveredList()函数通过关键字获取倒排拉链 InvertedList* GetInveredList(std::string word) {auto iter inverted_index.find(word);//查找关键字是否被建立倒排索引iter类型为pairbool,if(iter inverted_index.end())//关键字不在倒排索引内{std::cerrthis word not exists invertedliststd::endl;return nullptr;}return iter-second;//返回倒排拉链 }接下来写BuildIndex()函数建立索引正排索引和倒排索引 bool BuildIndex(const std::string raw_path)//parser.cc处理后保存的大文本文件的路径 {std::ifstream ifs(raw_path,std::ios::in | std::ios::binary);//保存文件时是按照二进制方式的if(!ifs.is_open()){std::cerrraw_pathopen is errorstd::endl;return false;}int count 0;std::string line;while(std::getline(ifs,line))//按行读取一个个DocInfo序列化的字符串title\3content\3url。{ForwardElem* fe BuildForwardIndex(line);//根据读取的字符串建立正排索引。if(fe nullptr)//建立失败{std::cerrbuild line errorstd::endl;continue;}BuildInvertedIndex(*fe);//根据正排文档建立倒排索引count;std::cout当前已建立的索引: std::to_string(count)std::endl;//为了观察建立索引的进度}return true; }最后完成BuildForwardIndex()函数和BuildInvertedIndex()函数就大功告成了。 先BuildForwardIndex()函数 BuildForwardIndex函数的主要目的是将title\3content\3url这样的字符串转化为DocInfo这样的结构体所以需要采用字符串截取操作可以使用find substr但比较麻烦。不推荐使用strtok因为它会改变原始字符串。这里我选择使用Boost库中的split函数。 我把split函数写到工具类(utill.hpp)中 namespace StringUtil {static void Split(const std::string line,std::vectorstd::string* result,const std::string sep){boost::split(*result,line,boost::is_any_of(sep),boost::token_compress_on);} }这里的split第三个参数是处理特殊情况比如title\3\3\3\3\3\3content\3url 如果写为boost::token_compress_off分隔结果中会有多个空串因此强烈建议使用token_compress_on ForwardElem* BuildForwardIndex(const std::string line) {//linetitle\3content\3\urlstd::vectorstd::string results;const std::string sep \3;ns_util::StringUtil::Split(line,results,sep);//根据sep切分字符串。//results为[0]title、[1]content、[2]urlif(results.size() ! 3)//不为3则出错处理{return nullptr;}ForwardElem fe;fe.title results[0];fe.content results[1];fe.url results[2];fe.id forward_index.size();//一开始size()为0forward_index.push_back(std::move(fe));//move是减少拷贝的操作return forward_index.back();//将fe返回给倒排索引建立倒排索引。//注意这里不能用fe,因为上面用了move()这是一个大坑 }编写BuildInvertedIndex()函数 该函数的需要对DocInfo正排文档进行title和content的分词来建立倒排索引这里我使用的是cppjieba分词。 第一步 安装cppjieba网址https://github.com/yanyiwu/cppjieba 然后git clone下来即可 其中include/cppjieba包含我们需要的.h头文件但这里有个小bug需要将deps/limon目录拷贝到include/cppjieba目录下才能正常使用。 现在我们开始使用cppjieba中的test文件进行小测试 用vim打开这个demo.cpp看看情况 由于我把demo.cpp拷贝到了另外一个目录这里面使用的相对路径就都失效了需要我们解决。同时还需要包含、等头文件。 如何解决相对路径失效的问题 答案使用软连接解决。 如下 然后就可以通过g编译运行demo.cpp了 搞定cppjieba分词组件后我们需要把它放到一个指定路径下作为项目的第三方库路径这样项目看起来更工程点。 比如~/third-lib 使用时通过软连接引用即可。 接下来我们需要用cppjieba完成一个易于调用的分词函数我把它写到了工具类中。 namespace JiebaUtil {//下面5行都是用于分词的词库const char* const DICT_PATH ./dict/jieba.dict.utf8;const char* const HMM_PATH ./dict/hmm_model.utf8;const char* const USER_DICT_PATH ./dict/user.dict.utf8;const char* const IDF_PATH ./dict/idf.utf8;const char* const STOP_WORD_PATH ./dict/stop_words.utf8;cppjieba::Jieba jieba(DICT_PATH,HMM_PATH,USER_DICT_PATH,IDF_PATH,STOP_WORD_PATH);std::unordered_mapstd::string, bool stop_words;void CutString(const std::string line,std::vectorstd::string* out)//完成分词函数{jieba.Cut(line,*out);} }以后我们只需要调用工具类中的CutString即可完成分词。 分词工具搞定后接下来我们需要完成的是BuildInvertedIndex函数的编写这个函数是重点中的难点。 bool BuildInvertedIndex(const ForwardElem fe) {struct word_cnt//记录某个关键字在title中出现次数和在content中出现次数。{int title;int content;word_cnt():title(0),content(0){};};std::unordered_mapstd::string,word_cnt word_map;//词频统计//对title进行分词std::vectorstd::string title_words;ns_util::JiebaUtil::CutString(fe.title,title_words);//统计title词频for(auto word : title_words)//这里需要用传值否则会改变title_words{boost::to_lower(word); //将word转化为小写全部建立小写的索引查询时转换成小写再查询索引word_map[word].title;}//对content进行分词std::vectorstd::string content_words;ns_util::JiebaUtil::CutString(fe.content,content_words);//统计content词频for(auto word : content_words){boost::to_lower(word);//将word转化为小写全部建立小写的索引查询时转换成小写再查询索引word_map[word].content;}#define X 10 #define Y 1for(auto word : word_map){InvertedElem ie;//构建倒排文档ie.id fe.id;//这是为什么DocInfo结构体为什么有id成员变量的原因方便构建InvertedElemie.weight X * word.second.title Y * word.second.content;//权重设置ie.word word.first;//设置关键字InvertedList iel inverted_index[ie.word]; //将相同关键字的倒排文档插入至倒排拉链iel.push_back(std::move(ie));//构建关键字和倒排拉链的映射}return true; }终于完成了Index.hpp的编写但还有一点完善的是为了项目的工程性我们需要把index.hpp设置成单例模式 #pragma once #includeiostream #includestring #includevector #includeunordered_map #includefstream #includeutil.hpp #includemutexnamespace ns_index {struct ForwardElem{std::string title;std::string content;std::string url;uint64_t id;};struct InvertedElem{uint64_t id;int weight;std::string word;};typedef std::vectorInvertedElem InvertedList;class Index{private:std::vectorForwardElem forward_index;std::unordered_mapstd::string,InvertedList inverted_index;private:static Index* instance;static std::mutex mtx;Index(){};Index(const Index) delete;Index operator(const Index) delete;public:~Index(){};public:static Index* GetInstance(){if(instance nullptr){mtx.lock();if(instance nullptr){instance new Index();}mtx.unlock();}return instance;}ForwardElem* GetForwardIndex(uint64_t id){if(id forward_index.size()){std::cerrid index beyond rangestd::endl;return nullptr;}return forward_index[id];}InvertedList* GetInveredList(std::string word){auto iter inverted_index.find(word);if(iter inverted_index.end()){std::cerrthis word not exists invertedliststd::endl;return nullptr;}return iter-second;}bool BuildIndex(const std::string raw_path){std::ifstream ifs(raw_path,std::ios::in | std::ios::binary);if(!ifs.is_open()){std::cerrraw_pathopen is errorstd::endl;return false;}int count 0;std::string line;while(std::getline(ifs,line)){ForwardElem* fe BuildForwardIndex(line);if(fe nullptr){std::cerrbuild line errorstd::endl;continue;}BuildInvertedIndex(*fe);count;std::cout当前已建立的索引: std::to_string(count)std::endl;;}return true;}private:ForwardElem* BuildForwardIndex(const std::string line){std::vectorstd::string results;const std::string sep \3;ns_util::StringUtil::Split(line,results,sep);if(results.size() ! 3){return nullptr;}ForwardElem fe;fe.title results[0];fe.content results[1];fe.url results[2];fe.id forward_index.size();forward_index.push_back(std::move(fe));return forward_index.back();//注意这里不能用fe,因为上面用了move()}bool BuildInvertedIndex(const ForwardElem fe){struct word_cnt{int title;int content;word_cnt():title(0),content(0){};};std::unordered_mapstd::string,word_cnt word_map;//对title进行分词std::vectorstd::string title_words;ns_util::JiebaUtil::CutString(fe.title,title_words);//统计title词频for(auto word : title_words){boost::to_lower(word); word_map[word].title;}//对content进行分词std::vectorstd::string content_words;ns_util::JiebaUtil::CutString(fe.content,content_words);//统计content词频for(auto word : content_words){boost::to_lower(word);word_map[word].content;}#define X 10 #define Y 1for(auto word : word_map){InvertedElem ie;ie.id fe.id;ie.weight X * word.second.title Y * word.second.content;ie.word word.first;InvertedList iel inverted_index[ie.word]; iel.push_back(std::move(ie));}return true;}};Index* Index::instance nullptr;std::mutex Index::mtx; }6.3为用户提供搜索服务 思路先写本地的Search服务再转为网络服务。 本地服务Search.hpp #pragma once #includeiostream #includestring #includevector #includeunordered_map #includeindex.hpp #includejsoncpp/json/json.hnamespace ns_searcher {struct InvertedInfoPrint{uint64_t id;int weight;std::vectorstd::string words;InvertedInfoPrint():id(-1),weight(0){};};class Searcher{private:ns_index::Index* index;//索引服务public:void InitSearcher(const std::string input)//初始化Searcher{index ns_index::Index::GetInstance();std::cout获取单例成功!std::endl;index-BuildIndex(input);std::cout构建索引成功!std::endl;}void Search(const std::string query,std::string* json_string)//根据搜索内容返回Json字符串{//对query进行分词std::vectorstd::string words;ns_util::JiebaUtil::CutString(query,words);//对query进行分词std::unordered_mapuint64_t,InvertedInfoPrint for_unique;//对倒排文档去重而创建std::vectorInvertedInfoPrint InvertedList_all;//新的倒排拉链for(auto word : words){boost::to_lower(word);//查找索引前需要转为小写因为索引的关键字都是小写的ns_index::InvertedList* list index-GetInveredList(word);//获取倒排拉链if(list nullptr){continue;}for(auto item : *list)//对组合的倒排拉链进行去重{InvertedInfoPrint ele for_unique[item.id];ele.id item.id;ele.weight item.weight;//相同文档权重相加ele.words.push_back(item.word);}}for(const auto e : for_unique)//得到新的倒排拉链{InvertedList_all.push_back(std::move(e.second));//}std::sort(InvertedList_all.begin(),InvertedList_all.end(),\[](const InvertedInfoPrint e1,const InvertedInfoPrint e2){return e1.weight e2.weight;});//对新的倒排拉链进行权重降序排序//完成序列化与反序列化Json::Value root;for(auto e : InvertedList_all)//根据倒排文档得到正排文档{ns_index::ForwardElem* fe index-GetForwardIndex(e.id);//查找正排索引if(fe nullptr){continue;}Json::Value ele;ele[title] fe-title;ele[desc] GetDesc(fe-content,e.words[0]);//根据内容得到摘要这里是为什么InvertedEle中有word成员变量的原因。ele[url] fe-url;root.append(ele);}Json::FastWriter w;*json_string w.write(root);}//摘要设计找到word出现在content的第一个位置向前找50个字符再向后找100个字符构成摘要。如果前面不够50个则从0下标开始截取。如果后面不够100个则截取到结尾。std::string GetDesc(const std::string content,const std::string word){const int prev 50;const int next 100;//这里有一个大坑不能使用string.find()查找因为它不能忽略大小写查找。如果你查找的是filesystem则找到的只有filesystem。auto iter std::search(content.begin(),content.end(),word.begin(),word.end(),\[](int x,int y){return std::tolower(x) std::tolower(y);});if(iter content.end()){return None1;}int pos std::distance(content.begin(),iter);//得到找到位置的下标int start 0;int end content.size()-1;if(pos-prev start){start pos-prev;}if(pos next end){end pos next;}if(start end){return None2;}std::string desc content.substr(start,end-start);desc ....;return desc;}}; } 提供本地服务 #includesearcher.hppconst std::string input ./data/raw/raw.txt; char buf[256] {0}; std::string query; std::string json_string;int main() {ns_searcher::Searcher s;s.InitSearcher(input);std::coutPlease Enter Your Query# ;fgets(buf,sizeof(buf)-1,stdin);//注fgets会读取\n给buf。buffer[strlen(buffer)-1] 0;//去掉buf里面的\nquery buf;s.Search(query,json_string);std::coutjson_stringstd::endl; }提供网络服务 这里使用现成的http服务cpphttp-lib 编写http_server.cc #includesearcher.hpp #includecpp-httplib/httplib.h #includestring #includelog.hppconst std::string input ./data/raw/raw.txt; const std::string root_path ./www.root;int main() {ns_searcher::Searcher searcher;//创建serachersearcher.InitSearcher(input);//初始化searcherdaemonize();//将服务守护进程化Log log;log.enable();//设置日志httplib::Server svr;svr.set_base_dir(root_path.c_str());//设置web根目录svr.Get(/s,[searcher](const httplib::Request req,httplib::Response res){if(!req.has_param(word))//查看url是否携带word{res.set_content(必须要有搜索关键字,text/plain; charset utf-8);//响应报文return;}std::string word req.get_param_value(word);//获取word的value值std::string json_string;searcher.Search(word,json_string);//获取Json字符串res.set_content(json_string,application/json);//将Json串返回给客户端});std::cout服务启动成功!!std::endl;svr.listen(0.0.0.0,8081);//绑定和监听return 0; } 将http_server.cc守护进程化 #pragma once#include cstdio #include iostream #include signal.h #include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.hvoid daemonize() {int fd 0;// 1. 忽略SIGPIPEsignal(SIGPIPE, SIG_IGN);// 2. 更改进程的工作目录// chdir();// 3. 让自己不要成为进程组组长if (fork() 0)exit(1);// 4. 设置自己是一个独立的会话setsid();// 5. 重定向0,1,2if ((fd open(/dev/null, O_RDWR)) ! -1) // fd 3{dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);// 6. 关闭掉不需要的fdif(fd STDERR_FILENO) close(fd);} }6.4编写前端代码 这里的前端代码看看就行主要是为了展示项目。 !DOCTYPE html html langen headmeta charsetUTF-8meta http-equivX-UA-Compatible contentIEedgemeta nameviewport contentwidthdevice-width, initial-scale1.0script srchttp://code.jquery.com/jquery-2.1.1.min.js/scripttitleboost 搜索引擎/titlestyle* {margin: 0;padding: 0;}html,body {height: 100%;}.container {width: 800px;margin: 0px auto;margin-top: 15px;}.container .search {width: 100%;height: 52px;}.container .search input {float: left;width: 600px;height: 50px;border: 1px solid black;border-right: none;padding-left: 10px;color: #CCC;font-size: 14px;}.container .search button {float: left;width: 150px;height: 52px;background-color: #4e6ef2;color: #FFF;font-size: 19px;font-family:Georgia, Times New Roman, Times, serif;}.container .result {width: 100%;}.container .result .item {margin-top: 15px;}.container .result .item a {display: block;text-decoration: none;font-size: 20px;color: #4e6ef2;}.container .result .item a:hover {text-decoration: underline;}.container .result .item p {margin-top: 5px;font-size: 16px;font-family:Lucida Sans, Lucida Sans Regular, Lucida Grande, Lucida Sans Unicode, Geneva, Verdana, sans-serif;}.container .result .item i{display: block;font-style: normal;color: green;}/style /head bodydiv classcontainerdiv classsearchinput typetext value请输入搜索关键字button onclickSearch()搜索一下/button/divdiv classresult/div/divscriptfunction Search(){let query $(.container .search input).val();console.log(query query); //console是浏览器的对话框可以用来进行查看js数据//2. 发起http请求,ajax: 属于一个和后端进行数据交互的函数JQuery中的$.ajax({type: GET,url: /s?word query,success: function(data){console.log(data);BuildHtml(data);}});}function BuildHtml(data){// 获取html中的result标签let result_lable $(.container .result);// 清空历史搜索结果result_lable.empty();for( let elem of data){// console.log(elem.title);// console.log(elem.url);let a_lable $(a, {text: elem.title,href: elem.url,// 跳转到新的页面target: _blank});let p_lable $(p, {text: elem.desc});let i_lable $(i, {text: elem.url});let div_lable $(div, {class: item});a_lable.appendTo(div_lable);p_lable.appendTo(div_lable);i_lable.appendTo(div_lable);div_lable.appendTo(result_lable);}}/script /body /html6.5日志的补充 编写log.hpp #pragma once#include cstdio #include ctime #include cstdarg #include cassert #include cassert #include cstring #include cerrno #include stdlib.h #include sys/types.h #include sys/stat.h #include fcntl.h#define DEBUG 0 #define NOTICE 1 #define WARINING 2 #define FATAL 3const char *log_level[] {DEBUG, NOTICE, WARINING, FATAL};#define LOGFILE serverTcp.logclass Log { public:Log():logFd(-1){}void enable(){umask(0);logFd open(LOGFILE, O_WRONLY | O_CREAT | O_APPEND, 0666);assert(logFd ! -1);dup2(logFd, 1);dup2(logFd, 2);}~Log(){if(logFd ! -1) {fsync(logFd);close(logFd);}} private:int logFd; };// logMessage(DEBUG, %d, 10); void logMessage(int level, const char *format, ...) {assert(level DEBUG);assert(level FATAL);char *name getenv(USER);char logInfo[1024];va_list ap; va_start(ap, format);vsnprintf(logInfo, sizeof(logInfo) - 1, format, ap);va_end(ap); // ap NULLFILE *out (level FATAL) ? stderr : stdout;fprintf(out, %s | %u | %s | %s\n,log_level[level],(unsigned int)time(nullptr),name nullptr ? unknow : name,logInfo);fflush(out); // 将C缓冲区中的数据刷新到OSfsync(fileno(out)); // 将OS中的数据尽快刷盘 }6.6项目扩展 建立全站搜索设计一个在线更新的方案信号爬虫完成整个服务器的设计不使用组件而是自己设计一下对应的各种方案在我们的搜索引擎中添加竞价排名热次统计智能显示搜索关键词字典树优先级队列设置登陆注册引入对mysql的使用
文章转载自:
http://www.morning.kqbwr.cn.gov.cn.kqbwr.cn
http://www.morning.nnwpz.cn.gov.cn.nnwpz.cn
http://www.morning.wyjpt.cn.gov.cn.wyjpt.cn
http://www.morning.hsdhr.cn.gov.cn.hsdhr.cn
http://www.morning.hxrfb.cn.gov.cn.hxrfb.cn
http://www.morning.xyrss.cn.gov.cn.xyrss.cn
http://www.morning.djbhz.cn.gov.cn.djbhz.cn
http://www.morning.zyffq.cn.gov.cn.zyffq.cn
http://www.morning.pjtw.cn.gov.cn.pjtw.cn
http://www.morning.wynnb.cn.gov.cn.wynnb.cn
http://www.morning.ykshx.cn.gov.cn.ykshx.cn
http://www.morning.gwdkg.cn.gov.cn.gwdkg.cn
http://www.morning.kclkb.cn.gov.cn.kclkb.cn
http://www.morning.rmrcc.cn.gov.cn.rmrcc.cn
http://www.morning.nqypf.cn.gov.cn.nqypf.cn
http://www.morning.yzmzp.cn.gov.cn.yzmzp.cn
http://www.morning.nzqmw.cn.gov.cn.nzqmw.cn
http://www.morning.smrkf.cn.gov.cn.smrkf.cn
http://www.morning.wfspn.cn.gov.cn.wfspn.cn
http://www.morning.qxkcx.cn.gov.cn.qxkcx.cn
http://www.morning.txnqh.cn.gov.cn.txnqh.cn
http://www.morning.lnbcg.cn.gov.cn.lnbcg.cn
http://www.morning.rckdq.cn.gov.cn.rckdq.cn
http://www.morning.qclmz.cn.gov.cn.qclmz.cn
http://www.morning.hsjfs.cn.gov.cn.hsjfs.cn
http://www.morning.lmxrt.cn.gov.cn.lmxrt.cn
http://www.morning.ctswj.cn.gov.cn.ctswj.cn
http://www.morning.fkgcd.cn.gov.cn.fkgcd.cn
http://www.morning.fznj.cn.gov.cn.fznj.cn
http://www.morning.krxzl.cn.gov.cn.krxzl.cn
http://www.morning.huarma.com.gov.cn.huarma.com
http://www.morning.mcwgn.cn.gov.cn.mcwgn.cn
http://www.morning.pngdc.cn.gov.cn.pngdc.cn
http://www.morning.lgnbr.cn.gov.cn.lgnbr.cn
http://www.morning.wprxm.cn.gov.cn.wprxm.cn
http://www.morning.sqgsx.cn.gov.cn.sqgsx.cn
http://www.morning.drbwh.cn.gov.cn.drbwh.cn
http://www.morning.dnconr.cn.gov.cn.dnconr.cn
http://www.morning.wslpk.cn.gov.cn.wslpk.cn
http://www.morning.xcjwm.cn.gov.cn.xcjwm.cn
http://www.morning.pghfy.cn.gov.cn.pghfy.cn
http://www.morning.brscd.cn.gov.cn.brscd.cn
http://www.morning.nqbkb.cn.gov.cn.nqbkb.cn
http://www.morning.5-73.com.gov.cn.5-73.com
http://www.morning.zlsmx.cn.gov.cn.zlsmx.cn
http://www.morning.hkysq.cn.gov.cn.hkysq.cn
http://www.morning.wprxm.cn.gov.cn.wprxm.cn
http://www.morning.nxkyr.cn.gov.cn.nxkyr.cn
http://www.morning.lizpw.com.gov.cn.lizpw.com
http://www.morning.xzsqb.cn.gov.cn.xzsqb.cn
http://www.morning.pzlcd.cn.gov.cn.pzlcd.cn
http://www.morning.lltdf.cn.gov.cn.lltdf.cn
http://www.morning.kwblwbl.cn.gov.cn.kwblwbl.cn
http://www.morning.pgggs.cn.gov.cn.pgggs.cn
http://www.morning.fdzzh.cn.gov.cn.fdzzh.cn
http://www.morning.nzdks.cn.gov.cn.nzdks.cn
http://www.morning.rccbt.cn.gov.cn.rccbt.cn
http://www.morning.qhqgk.cn.gov.cn.qhqgk.cn
http://www.morning.lzqxb.cn.gov.cn.lzqxb.cn
http://www.morning.ffbp.cn.gov.cn.ffbp.cn
http://www.morning.phgz.cn.gov.cn.phgz.cn
http://www.morning.nmpdm.cn.gov.cn.nmpdm.cn
http://www.morning.wynnb.cn.gov.cn.wynnb.cn
http://www.morning.xkpjl.cn.gov.cn.xkpjl.cn
http://www.morning.hnhkz.cn.gov.cn.hnhkz.cn
http://www.morning.zstbc.cn.gov.cn.zstbc.cn
http://www.morning.qmsbr.cn.gov.cn.qmsbr.cn
http://www.morning.sqmbb.cn.gov.cn.sqmbb.cn
http://www.morning.tgwfn.cn.gov.cn.tgwfn.cn
http://www.morning.nkpls.cn.gov.cn.nkpls.cn
http://www.morning.gjzwj.cn.gov.cn.gjzwj.cn
http://www.morning.nqpxs.cn.gov.cn.nqpxs.cn
http://www.morning.wqpm.cn.gov.cn.wqpm.cn
http://www.morning.rccbt.cn.gov.cn.rccbt.cn
http://www.morning.wrlqr.cn.gov.cn.wrlqr.cn
http://www.morning.mhnxs.cn.gov.cn.mhnxs.cn
http://www.morning.rzbcz.cn.gov.cn.rzbcz.cn
http://www.morning.mtjwp.cn.gov.cn.mtjwp.cn
http://www.morning.lanyee.com.cn.gov.cn.lanyee.com.cn
http://www.morning.qflwp.cn.gov.cn.qflwp.cn
http://www.tj-hxxt.cn/news/236706.html

相关文章:

  • 佛山外贸网站精品课程网站设计代码
  • 专业做二手健身器材的是什么网站建网站难不难
  • 一个可以做网站网站建设的硬件平台
  • 爱的网站歌曲克隆的网站怎么做数据库
  • 农业网站模板WordPress机票旅游网站建设
  • 重庆sem网站推广电子商城系统平台
  • 有哪些网站可以做任务浏览器搜索引擎大全
  • 一站式做网站系统icon图标素材下载网站
  • 网站目录遍历茂名seo快速排名外包
  • 全屋定制怎么样做网站学校网站资源库建设和资源上传
  • 给设计网站做图会字体侵权吗家具网站策划书
  • 中国最好网站建设公司网站设计制作一般多少钱
  • 哈尔滨网站建设自助建站app store下载正版
  • 公明网站建设东莞最穷的三个镇
  • 网站设计与制作是做什么工作千万别学广告学
  • 有几个网站如何做外贸wordpress好的插件推荐
  • 孝感网站建设西安建设网
  • 网站制作的预算附近做网站
  • 自己做的网站怎么在局域网中访问安卓系统app开发公司
  • 自己怎样做海外网站网络管理系统密码
  • 湖南鸿源电力建设有限公司网站建设是哪里的
  • 西安建站公司模板高端网页定制
  • 广东公司网站建设企业旅游网站建设目的
  • 唐山石家庄做网站哪家好wordpress点击网页效果
  • 河南省水利建设厅网站外贸网店建站模板
  • 阿里巴巴国际站运营模式图片制作二维码的方法
  • 什么网站做简历最好wordpress为艾迪
  • 网站标题怎么设置电商平台代运营
  • 网站维护的重要性建网站容易吗
  • 秦皇岛网站村网通为每个农村建设了网站