【项目】Boost搜索引擎

这篇具有很好参考价值的文章主要介绍了【项目】Boost搜索引擎。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Boost搜索引擎

1. 项目的相关背景

研发搜索引擎的公司,如百度、搜狗、360搜索,还有各大网站各种客户端也提供搜索功能

为什么选择实现Boost搜索引擎

1)因为Boost官方网站是没有搜索功能的,所以我们可以为Boost实现一个站内搜索引擎,虽然官方提供了boost相关的一些方法,标准库中的一些接口,但是我们想看到官方文档成本比较高,所以我们可以自己做一个站内搜索

2)自行实现一个全网搜索引擎难度极大,是十分困难的,但是实现站内搜索,也就是只搜索网站内的内容,这样搜索的内容更垂直(即:搜索的内容有很强的相关性 ),数据量更小,也可以达到以小见大的效果,


对于搜索结果,基本包含三个部分:网页标题,网页内容摘要,目标网页地址

【项目】Boost搜索引擎

我们还可以发现,有时候我们可以搜到一个广告推销,本质上,广告是搜索引擎的一种盈利的方式,每一个客户在用搜索引擎的时候,都要搜索关键字,所以这些搜索引擎可以出售这些关键字, 谁给的钱越多,谁的搜索结果就越靠前


2.搜索引擎的相关宏观原理

【项目】Boost搜索引擎

程序跑起来,一定是在内存当中跑

1)还没有进行搜索之前,首先需要在全网当中抓取网页,假设放在data目录下,里面包含了抓取的所有的网页信息

2)把网页抓取下来之后,对内容进行去标签,数据清理, 只保留网页的内容,标题,网页的url

3)searcher建立索引,既然要搜索肯定要建立索引:这个索引作用是为了加速查找

4)开始搜索,服务器一旦启动,浏览器就需要通过http请求的方式进行搜索任务, (本质还是提交http请求,然后在我们的服务端执行搜索任务)


3. 相关技术栈和项目环境

技术栈

  • 后端:C/C++,C++11,STL,Boost标准库,Jsoncpp,cppjieba,cpp-httplib
    • jsoncpp:对响应的内容完成序列化的操作
    • cppjieba:对搜索关键字分词,组合成各种搜索的关键字然后进行文档搜索
    • cpp-httplib:开源库,构建http服务器
  • 前端:html5,jQuery,Ajax

项目环境

Centos7云服务器,vim/gcc(g++)/Makefile,vscode


4.搜索引擎具体原理-正排索引 && 倒排索引

搜索引擎必然要对内容建立索引,才能更快的搜索和返回,有两种索引:正排索引和倒排索引,以如下内容举例:

有如下两个文档,我们对这两个文档内容建立索引:

文档1:雷军买了四斤小米 文档2:雷军发布了小米手机

正排索引

建立正排索引本质就是建立文档ID和文档内容的对应关系,正排索引就是根据文档ID找到文档内容

文档ID 文档内容
1 雷军买了四斤小米
2 雷军发布了小米手机

对文档分词

拿到文档首先要对其编号,其次对文档内容进行分词,也就是得到文档内的关键字,为的是建立倒排索引和方便查找

  • 对于文档1:雷军买了四斤小米 可以分词为:雷军/买/了/四斤/小米/四斤小米
  • 对于文档2:雷军发布了小米手机 可以分词为:雷军/发布/了/小米/小米手机

其中对于“了”、“呢”、“的”、“啊”,这些词都被称为停止词或暂停词,这些词对我们建立索引是没有意义的,一般我们在分词的时候可以不考虑,因为这些词的出现频率太高了,如果保留下来,搜索的时候区分唯一性的价值不大,会增加建立索引的成本,乃至于增加搜索的成本

倒排索引

倒排索引就是根据文档内容,进行分词,整理具有唯一性不重复的关键字,再根据关键字找到关联文档ID的方案,简单来说就是:根据关键字,找到其在哪些文档ID出现过

关键字(具有唯一性) 文档ID(在哪些文档出现过)
雷军 1,2
1
四斤 1
小米 1,2
四斤小米 1
发布 2
小米手机 2

模拟一次搜索的过程:

用户输入:小米 -> 在倒排索引中查找该关键字,提取出文档ID (1, 2) -> 根据文档ID查出正排索引,找到文档内容 ->获取文档的标题、内容、描述、URL -> 对文档结果进行摘要 -> 构建响应并返回

  • 可能一个关键字出现在多个文档当中,所以我们可以根据谁的权重更高,就把谁的文档放在前面展示

5.数据去标签与数据清洗的模块

获取数据源:

boost官网: https://www.boost.org/

1.下载搜索的数据源: https://www.boost.org/users/history/version_1_78_0.html

【项目】Boost搜索引擎

2.通过拖拽/re -E指令,把压缩包上传到云服务器当中

3.然后进行解包: tar xzf 压缩包名字

【项目】Boost搜索引擎

实际上我们查的大部分的手册,都是在doc目录下的html

【项目】Boost搜索引擎


【项目】Boost搜索引擎

这个就是标准库对应的各种boost组件对应的手册内容,就是一个个的网页信息 ,就是我们的数据源


创建一个名字为data的目录,里面包含一个input目录,input里面放我们的数据源, 把上述的数据源拷贝到input里面

【项目】Boost搜索引擎

目前我们只需要boost_1_78_0/doc/html目录下的html文件,用它来进行建立索引


认识标签和去标签

现在我们首先将数据源中的各个文档去标签化,HTML 是标签化语言,所有的语句都被一对标签包裹起来,由左右尖括号括起来的就是标签,对数据本身是无意义的,所以我们首先要将其去掉

  • 一般标签都是成对出现的,标签中的属性信息也是不需要的,只有标签内的数据是有用数据
<!--例子-->
<title>Chapter 37. Boost.STLInterfaces</title> 
<link rel="stylesheet" href="../../doc/src/boostbook.css" type="text/css">
<td align="center"><a href="../../index.html">Home</a></td>

我们可以创建一个目录存放清洗干净的html文档

【项目】Boost搜索引擎

其中:input当中放的是:原始的html文档,也就是数据源 raw_html/raw.txt当中放的是:去标签之后的干净文档内容


我们可以看一下当前有多少个html数据源:

【项目】Boost搜索引擎

我们当前的目标就是:把每个文档都进行去标签,把清洗后的内容写到同一个文件中, 写入到文件当中,一定要考虑下一次在读取的时候也要方便操作

  • 我们选择的方案是: html文件内部:以\3分割标题,内容,链接 html文档和html文档之间以\n区分
类似:title\3content\3url \n title\3content\3url \n title\3content\3url \n ...

这样我们使用getline(ifsream, line),一次读取一行内容,相当于直接获取一个html文档经过清洗之后的全部内容:title\3content\3url

注意:这里只需要使用两个\3即可,不需要使用三个\3:title\3content\3url\3 \n,因为两个分隔符就可以分隔三个东西


问题:为什么使用\3作为分隔符

首先我们要知道,控制字符是不可以显示的,即下述绿色框框中的(其ascii码值在0~31,127)的字符,而有一些字符是打印字符,现在的我们的文档内容是属于打印字符的范畴

我们使用\3作为分隔符,因为\3是控制字符是控制字符,不显示,不会污染我们的文档内容,当然,我们也可以使用\4之类的不可显字符作为分割字符

【项目】Boost搜索引擎

注意: \3,在文档当中是以^c 即出现


关于参数的说明

const &	表示是输入型参数
&		表示是输入输出型参数
* 		表示是输出型参数

编写parser.cc

基本框架

主要过程:

1.获取所有的带路径的文件名数据源html,进行解析,解析成:title,content,url的形式

2.把解析后的数据放到清洗后数据存放位置

//首先我们涉及到读取文件的动作,我们先把将要读取的文件的路径定义出来,方便我们进行读取
const std::string src_path = "data/input"; // html网页数据源路径,input下面放的是所有的html网页
const std::string output = "data/raw_html/raw.txt"; // 文档数据清洗之后的保存路径

// 解析html文档,每一个html文档都被拆成下面的样子:
typedef struct DocInfo_t 
{
   
    std::string title;       // 文档标题
    std::string content;     // 文档内容
    std::string url;         // 该文档在官网中的url
}DocInfo_t;

//先声明
bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list);
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results);
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output);


int main()
{
   
    std::vector<std::string> files_list;  //保存所有的带路径的html文件
    //第一步:递归式的把src_path路径下的每个html文件名带路径,保存到files_list中
    //为什么这样做? 方便后期进行一个一个的文件进行读取
    if(!EnumFile(src_path, &files_list)) //枚举所有带路径的文件名
    {
   
        std::cerr << "enum file name error!" << std::endl;
        return 1;
    }    

    //第二步:文件内容的解析
    std::vector<DocInfo_t> results;//存放每个文档解析完的内容-DocInfo_t结构
    if(!ParseHtml(files_list, &results)){
    //本质是读取files_list路径的文件内容并且解析
        std::cerr << "parse html error" << std::endl;
        return 2;
    }
    //第三步: 把解析完毕的各个文件内容写入到output路径对应的文件
    if(!SaveHtml(results, output)){
   
        std::cerr << "sava html error" << std::endl;
        return 3;
    }
    return 0;
}

枚举带路径的html文件

C++对文件系统的支持并不是很好,所以我们需要使用的是boost库的file system模块

#include <boost/filesystem.hpp> //引入boost库的file system模块

首先,我们需要在centos下安装在boost库 sudo yum install -y boost-devel

  • 这里需要注意的是:

【项目】Boost搜索引擎

我们进行搜索的是1.78版本的官方手册, 而我们用的boost库的版本是1.53 来写代码,这个是两码事


不建议直接将命名空间展开,防止命名冲突 ,减少冲突概率

第一步: 使用boost::filesystem命名空间下的path类型定义一个路径对象,并使用我们的参数路径进行初始化,需要判断当前路径释放是存在的

第二步:定义一个空迭代器,然后遍历该路径的文件,如果该文件不是普通文件就不处理,如果是普通文件还要判断后缀是不是以html结尾,

//第一个参数:所有文件保存的路径	第二个参数:输出型参数,枚举的文件名保存的位置
bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list)
{
   
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);//定义一个path类型的对象,遍历的时候就从这个路径下开始

    //判断root_path这个路径是否存在,如果不存在,就没有必要再往后走了
    if(!fs::exists(root_path))
    {
   
        //注意:这里不能直接打印root_path,因为它是用boost库的filesystem定义的对象,是一个对象!!
        std::cerr << src_path << " not exists" << std::endl;
        return false;
    }

    //对文件递归遍历,定义一个空的迭代器,用来进行判断递归结束
    fs::recursive_directory_iterator end;//迭代器是模拟指针的行为,这个空迭代器可以认为是nullptr
    //从root_path开始遍历  iter也是迭代器对象,用root_path构造它
    for(fs::recursive_directory_iterator iter(root_path); iter != end; iter++)
    {
   
        //对文件进行筛选,判断文件是否是普通文件:html都是普通文件
        if(!fs::is_regular_file(*iter))
        {
    
            continue;//不是普通文件就不处理
        }
        //这个普通文件还必须以html结尾,判断文件后缀
        //iter->path():返回当前迭代器所在的路径   extension方法:提取路径的后缀
        if(iter->path().extension() != ".html"){
    //判断 文件路径名的后缀是否符合要求
            continue; 
        }
        //测试: 来到这里,说明当前的路径一定是一个合法的,以.html结束的普通网页文件
        //std::cout << "debug: " << iter->path().string() << std::endl;
        
        //iter->path()得到的还是路径对象, string():将对象所对应的路径以字符串的形式呈现出来
        files_list->push_back(iter->path().string()); //将所有带路径的html保存在files_list,方便后续进行文本分析
    }
    return true;
}

解析html文件

大致框架

先读取文件的所有内容,再依次解析文件的 title、content、url,解析成功后拷贝至解析结果数组中

//第一个参数:保存所有带路径的html文件	第二个参数:输出型参数,解析结果数组
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
{
   
    //遍历所有html文件路径
    for(const std::string &file : files_list)
    {
   
        //1. 读取文件,file是带路径的html文件,打开它然后把它的内容全部读取出来
        std::string result; //把读取到的内容放到result当中
        if(!ns_util::FileUtil::ReadFile(file, &result)){
   
            continue;//读取失败,说明文件打开失败,不管它
        }
        //此时result当中保存的就是网页的内容
        DocInfo_t doc;
        //2. 解析指定的文件,提取title
        if(!ParseTitle(result, &doc.title)){
    
            continue;
        }
        //3. 解析指定的文件,提取content,本质就是去标签,只保留网页的内容
        if(!ParseContent(result, &doc.content)){
       
            continue;
        }   
        //4. 解析指定的文件路径,构建url
        if(!ParseUrl(file, &doc.url)){
   
            continue;
        }
        //来到这里,一定是完成了解析任务,当前文档的相关内容:title,content,url都保存在了doc结构体里面
        //results->push_back(doc) //细节:push_back,本质会发生拷贝,效率可能会比较低,所以直接移动
        results->push_back(std::move(doc)); //doc是临时对象,相当于是资源转移
    }
    return true;
}

注意:上述提取title和content, 传入的第一个参数是当前获得的html文件的内容,而构建ur传入的第一个参数则是当前html文件的路径,下面详解!


读取html文件

我们可以准备一个Util.hpp文件,存放我们的工具类,并且把内容都放在ns_util命名空间下面,所以我们可以单独把读取文件的函数写在一个类里面:

我们可以直接使用ifstream流进行读取,使用getline一次读取一行内容,放在out里面,最后记得关闭文件流!!!

class FileUtil
{
   
public:
    //第一个参数:该文件的路径 第二个参数:输出型参数,读到的文件内容返回出去
    static bool ReadFile(const std::string &file_path, std::string *out)
    {
   
        std::ifstream in(file_path, std::ios::in); //in表示读取
        if(!in.is_open()) //判断是否打开成功
        {
   
            std::cerr << "open file " << file_path << " error" << std::endl;
            return false;
        }
        //文件读取
        std::string line; //line保存的就是读取到的一行内容
        while(std::getline(in, line))
        {
    
            *out += line;
        }

        in.close(); //关闭文件
        return true;
    }
};

问题:如何理解getline读取到文件结束呢?

getline的返回值是一个流的引用,while循环判断是一个bool类型 ,本质是因为返回的对象当中重载了强制类型转化,意思就是判断对象的真假,这个对象里面就做了重载,重载强转之后得到的就是bool值


下面三个:提取title,content,构建url的三个函数都设置为static静态函数,我们只想在本源文件中有效

提取title

一般在html网页里面只有一对title,我们要做的就说提取这对标签里面的内容

【项目】Boost搜索引擎

其实很简单,只需要找到<title></title>位置即可, begin指向第一个<位置,end指向第二个<位置,然后begin跳过<title>个长度,来到标题的正文位置, 此时[begin,end)就是标题的内容

【项目】Boost搜索引擎

//第一个参数:file里面就是网页内容	第二个参数:输出型参数,存放当前网页的标签内容
static bool ParseTitle(const std::string &file, std::string *title)//解析网页的标题内容
{
   
    std::size_t begin = file.find("<title>"); 
    if(begin == std::string::npos){
   //说明没有找到,没办法解析
        return false;
    }
    std::size_t end = file.find("</title>");
    if(end == std::string::npos){
   //说明没有找到,没办法解析
        return false;
    }
    //begin移动到标题内容的起始位置
    begin += std::string("<title>").size();
    if(begin > end){
    
        return false;
    }
    *title = file.substr(begin, end - begin);//[begin,end)
    return true;
}

提取content

本质是进行去标签,在遍历整个html文件内容的时候,只要碰到>就意味着当前标签被处理完毕,只要碰到<就意味着即将处理新标签, 所以可以用枚举类型描述这2种状态,条件就绪更改状态,遇到内容时就插入到对应字符串中

//去标签(数据清洗)操作
static bool ParseContent(const std::string &file, std::string *content)
{
    
    //基于一个简易的状态机
    enum status{
     //枚举两种状态
        LABLE,//正在读的是标签里面的内容
        CONTENT//现在读取的是正常文本内容
    };

    enum status s = LABLE;//刚开始一定是标签 
    //file:就是整个html文件的内容(网页的内容), 按字符读取 
    for( char c : file)//这里没有加引用!!!
    {
    
        switch(s)    
        {
   
            //此时是标签状态,说明该字符可能是标签的一部分,如果是,则忽略,继续下次循环读取
            //但是需要注意:如果当前字符是右标签,说明下一次就是正常的文本内容
            case LABLE: 
                if(c == '>') s = CONTENT; //碰到右标签 ->标签状态结束
                break;
            //当前是文本状态,说明该字符可能是文本的一部分,如果是则插入到输出型参数当中
            //但是需要注意:如果当前字符是左标签,说明下一次就是读取标签的内容
            case CONTENT:
                if(c == '<') 
                    s = LABLE;//碰到左标签 ->内容读取完了,变换状态
                else //意味着此时是内容的状态,并且不是左标签
                {
   
                    //小细节:我们不想保留原始文件中的\n,因为我们想用\n作为html解析之后文本的分隔符
                    //所以我们把\n换成空格!所以这也是为什么不加引用的原因!否则就对源html文件做修改了
                    if(c == '\n') c = ' ';
                    content->push_back(c);//插入当前字符
                }
                break;
            default:
                break;
        }
    }
    return true;
}
构建url

需要注意:boost库的官方文档,和我们下载下来的文档的路径,是有对应关系的

【项目】Boost搜索引擎

官网URL样例:    https://www.boost.org/doc/libs/1_78_0/doc/html/accumulators.html
我们下载下来的url样例:  boost_1_78_0/doc/html/accumulators.html
我们拷贝到我们项目中的样例: data/input/accumulators.html

所以:我们只需要构造url的头部为:url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html"

然后url的尾部为: url_tail = /accumulators.html即可,也就是把我们项目当中的样例前面的 data/input删掉

url = url_head + url_tail ; 相当于形成了一个官网链接


//第一个参数:文件在当前项目的路径	第二个参数:输出型参数
static bool ParseUrl(const std::string &file_path, std::string *url) //构建官网链接
{
   
    std::string url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";//前缀
    //最初我们最开始已经定义了路径src_path = "data/input" 放的是所有的html数据源
    //file_path就相当于:data/input/accumulators.html 然后把前面的内容data/input去掉
    std::string url_tail = file_path.substr(src_path.size());
	//构成官网链接
    *url = url_head + url_tail;
    return true;
}

写法2: 不使用src_path

bool ParserUrl(const std::string& file, std::string* url)
{
   
    std::string url_head = "https://www.boost.org/doc/libs/1_80_0/doc/html/"; // 构建前缀,注意这里html后面加了个/
    int begin = file.rfind('/');//注意是从后往前找/ 
    if (begin == std::string::npos) {
   
        std::cout << "file suffix find error" << std::endl;
        return false;
    }
	//[begin+1,file的结束位置]就是后缀
    std::string url_tail(file, begin + 1);
    *url = url_head + url_tail;
    return true;
}

数据保存

把解析完毕的各个文件内容写入到output路径对应的文件, 以\3为文档内容之间的区分, 文档与文档之间用\n区分

title\3content\3url \n title\3content\3url \n title\3content\3url \n 

这样我们使用getline一次读取一行能够直接获得一个html文档的属性信息

//第一个参数:经过清洗之后的html文件属性信息数组	第二个参数:保存解析完毕的文件内容
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output)
{
   
    #define SEP '\3'  //分隔符
    //这里按照二进制方式进行写入,但是用文本写入也是可以的,但是这里因为有\3
    //二进制写的特点是:写入的是什么,文档里保存的就是什么,程序不会给我们做自动转化
    std::ofstream out(output, std::ios::out | std::ios::binary);//因为是要输出->所以是out,以二进制形式写
    if(!out.is_open()){
   //打开文件失败
        std::cerr << "open " << output << " failed!" << std::endl;
        return false;
    }
    //遍历数组
    for(auto &item : results) //item就是DocInfo_t结构,放的是一个html网页的标题,内容,链接
    {
   
        //文档内的分隔符是\3    文档间的分割方是\n
        std::string out_string;
        out_string = item.title;//标题
        out_string += SEP;// \3
        out_string += item.content;//内容
        out_string += SEP;// \3
        out_string += item.url; //链接
        out_string += '\n'; //一个文档和一个文档的分割符是\n

        //第一个参数:要写入的数据, 第二个参数:写入的字节数
        out.write(out_string.c_str(), out_string.size());//把字符串的内容写到文件

       /*
      std::string out_string(item._title + SEP + item._content + SEP + item._url + '\n');
      out.write(out_string.c_str(), out_string.size()); 
      */
    }
    out.close();//记得关闭流!
    return true;
}


用于debug函数

//for debug
static void ShowDoc( const DocInfo_t &doc)
{
   
    std::cout << "title: " << doc.title << std::endl;
    std::cout << "content: " << doc.content << std::endl;
    std::cout << "url: " << doc.url << std::endl;
}

可以在解析完html文件之后,调用该函数调试!由于内容过多,所以可以选择输出一个之后就break退出循环


运行结果:

【项目】Boost搜索引擎

我们之前看到的是,data/input目录下一共有8141个html文件, 现在处理之后一共有8141行,符合我们的预期!raw.txt文件内部的每一行都是一个网页对应的内容:标题\3内容\3网页链接 \n

【项目】Boost搜索引擎

\3对应的值就是^c ,属性之间的分隔符是\3显示为^C,文档之间的分隔符是\n不显示,并且我们可以赋值网址到浏览器搜索,看是否正确

可以直接 cat raw.txt | head -1 //查看前面的几行/后面的几行进行验证


如果url出现问题,调试,输出结果看是否符合预期,

例如:可能是src_path的路径为:data/input/导致的错误,因为我们的url_head最后面的html是没有加/的 此时我们对data/input/accumulators.html截取尾部获得的就是accumulators.html,所以拼接起来就是:https://www.boost.org/doc/libs/1_78_0/doc/htmlaccumulators.html

解决办法: src_path后面的路径不加/ ,直接就是data/input ,或者url_head的html后面加个/,也就是https://www.boost.org/doc/libs/1_78_0/doc/html/

【项目】Boost搜索引擎文章来源地址https://www.toymoban.com/news/detail-438571.html


汇总:parser.cc

#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp> //引入boost库
#include "util.hpp"

//首先我们涉及到读取文件的动作,我们先把将要读取的文件的路径定义出来,方便我们进行读取
const std::string src_path = "data/input"; // html网页数据源路径,input下面放的是所有的html网页
const std::string output = "data/raw_html/raw.txt"; // 文档数据清洗之后的保存路径

// 解析html文档,每一个html文档都被拆成下面的样子:
typedef struct DocInfo_t 
{
   
    std::string title;       // 文档标题
    std::string content;     // 文档内容
    std::string url;         // 该文档在官网中的url
}DocInfo_t;


//先声明
bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list);
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results);
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output);


int main()
{
   
    std::vector<std::string> files_list;  //保存所有的带路径的html文件
    //第一步:递归式的把src_path路径下的每个html文件名带路径,保存到files_list中
    //为什么这样做? 方便后期进行一个一个的文件进行读取
    if(!EnumFile(src_path, &files_list)) //枚举所有带路径的文件名
    {
   
        std::cerr << "enum file name error!" << std::endl;
        return 1;
    }    

    //第二步:文件内容的解析
    std::vector<DocInfo_t> results;//存放每个文档解析完的内容-DocInfo_t结构
    if(!ParseHtml(files_list, &results)){
    //本质是读取files_list路径的文件内容并且解析
        std::cerr << "parse html error" << std::endl;
        return 2;
    }
    //第三步: 把解析完毕的各个文件内容写入到output路径对应的文件
    if(!SaveHtml(results, output)){
   
        std::cerr << "sava html error" << std::endl;
        return 3;
    }
    return 0;
}
//第一个参数:所有文件保存的路径	第二个参数:输出型参数,枚举的文件名保存的位置
bool EnumFile(const std::string &src_path, std::vector<std::string> *files_list)
{
   
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);//定义一个path类型的对象,遍历的时候就从这个路径下开始

    //判断root_path这个路径是否存在,如果不存在,就没有必要再往后走了
    if(!fs::exists(root_path))
    {
   
        //注意:这里不能直接打印root_path,因为它是用boost库的filesystem定义的对象,是一个对象!!
        std::cerr << src_path << " not exists" << std::endl;
        return false;
    }

    //对文件递归遍历,定义一个空的迭代器,用来进行判断递归结束
    fs::recursive_directory_iterator end;//迭代器是模拟指针的行为,这个空迭代器可以认为是nullptr
    //从root_path开始遍历  iter也是迭代器对象,用root_path构造它
    for(fs::recursive_directory_iterator iter(root_path); iter != end; iter++)
    {
   
        //对文件进行筛选,判断文件是否是普通文件:html都是普通文件
        if(!fs::is_regular_file(*iter))
        {
    
            continue;//不是普通文件就不处理
        }
        //这个普通文件还必须以html结尾,判断文件后缀
        //iter->path():返回当前迭代器所在的路径   extension方法:提取路径的后缀
        if(iter->path().extension() != ".html"){
    //判断 文件路径名的后缀是否符合要求
            continue; 
        }
        //测试: 来到这里,说明当前的路径一定是一个合法的,以.html结束的普通网页文件
        //std::cout << "debug: " << iter->path().string() << std::endl;
        
        //iter->path()得到的还是路径对象, string():将对象所对应的路径以字符串的形式呈现出来
        files_list->push_back(iter->path().string()); //将所有带路径的html保存在files_list,方便后续进行文本分析
    }
    return true;
}

//第一个参数:经过清洗之后的html文件属性信息数组	第二个参数:保存解析完毕的文件内容
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output)
{
   
    #define SEP '\3'  //分隔符
    //这里按照二进制方式进行写入,但是用文本写入也是可以的,但是这里因为有\3
    //二进制写的特点是:写入的是什么,文档里保存的就是什么,程序不会给我们做自动转化
    std::ofstream out(output, std::ios::out | std::ios::binary);//因为是要输出->所以是out,以二进制形式写
    if(!out.is_open()){
   //打开文件失败
        std::cerr << "open " << output <

到了这里,关于【项目】Boost搜索引擎的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++项目】boost搜索引擎

    boost官网 Boost库是为C++语言标准库提供扩展的一些C++程序库的总称。 Boost库由Boost社区组织开发、维护。其目的是为C++程序员提供免费、同行审查的、可移植的程序库。Boost库可以与C++标准库完美共同工作,并且为其提供扩展功能。Boost库使用Boost License来授权使用,根据该协议

    2023年04月16日
    浏览(92)
  • 基于boost库的搜索引擎项目

    boost库是指一些为C++标准库提供扩展的程序库总称,但是boost网站中并没有为我们提供站内搜索功能,因此我们要想找到某一个类的用法还要一个个去找,因此我们这次的目的就是实现一个搜索引擎功能,提高我们获取知识的效率 比如百度,谷歌,360等,这些都是大型的搜索

    2024年03月14日
    浏览(91)
  • 基于boost准标准库的搜索引擎项目

    这是一个基于Web的搜索服务架构 客户端-服务器模型 :采用了经典的客户端-服务器模型,用户通过客户端与服务器交互,有助于集中管理和分散计算。 简单的用户界面 :客户端似乎很简洁,用户通过简单的HTTP请求与服务端交互,易于用户操作。 搜索引擎功能 :服务器端的

    2024年04月27日
    浏览(36)
  • 【Boost搜索引擎项目】Day1 项目介绍+去标签和数据清洗框架搭建

    🌈欢迎来到C++项目专栏 🙋🏾‍♀️作者介绍:前PLA队员 目前是一名普通本科大三的软件工程专业学生 🌏IP坐标:湖北武汉 🍉 目前技术栈:C/C++、Linux系统编程、计算机网络、数据结构、Mysql、Python 🍇 博客介绍:通过分享学习过程,加深知识点的掌握,也希望通过平台能

    2024年03月23日
    浏览(45)
  • [C++项目] Boost文档 站内搜索引擎(5): cpphttplib实现网络服务、html页面实现、服务器部署...

    在前四篇文章中, 我们实现了从文档文件的清理 到 搜索的所有内容: 项目背景: 🫦[C++项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍… 文档解析、处理模块 parser 的实现: 🫦[C++项目] Boost文档 站内搜索引擎(2): 文档文本解析模块parser的实现、如何对

    2024年02月13日
    浏览(47)
  • [C++项目] Boost文档 站内搜索引擎(3): 建立文档及其关键字的正排 倒排索引、jieba库的安装与使用...

    之前的两篇文章: 第一篇文章介绍了本项目的背景, 获取了 Boost 库文档 🫦[C++项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍… 第二篇文章 分析实现了 parser 模块. 此模块的作用是 对所有文档 html 文件, 进行清理并汇总 🫦[C++项目] Boost文档 站内搜

    2024年02月07日
    浏览(58)
  • [C++项目] Boost文档 站内搜索引擎(2): 文档文本解析模块parser的实现、如何对文档文件去标签、如何获取文档标题...

    在上一篇文章中, 已经从 Boost 官网获取了 Boost 库的源码. 相关文章: 🫦[C++项目] Boost文档 站内搜索引擎(1): 项目背景介绍、相关技术栈、相关概念介绍… 接下来就要编写代码了. 不过还需要做一些准备工作. 创建项目目录 所有的项目文件肯定要在一个目录下, 找一个位置执行下

    2024年02月14日
    浏览(46)
  • boost 搜索引擎

    done 公司:百度、搜狗、360搜索、头条新闻客户端 - 我们自己实现是不可能的! 站内搜索:搜索的数据更垂直,数据量其实更小 boost的官网是没有站内搜索的,需要我们自己做一个 首先在用户进行搜索之前,在公司的服务器server上,内存上有一个searcher服务,而我们想进行搜

    2024年02月11日
    浏览(51)
  • boost库搜索引擎

    Gitee仓库:boost库搜索引擎 市面上有很多搜索引擎例如Google、百度、360等,这些都是特别大的项目。 对于个人学习我们可以写一个 站内搜索 ,这个搜索的内容更加垂直,数据量更小,例如C++的文档The C++ Resources Network Google搜索显示内容: 客户端使用浏览器搜索向服务器发起

    2024年04月09日
    浏览(51)
  • Boost搜索引擎

    先说一下什么是搜索引擎,很简单,就是我们平常使用的百度,我们把自己想要所有的内容输入进去,百度给我们返回相关的内容.百度一般给我们返回哪些内容呢?这里很简单,我们先来看一下. 这里我们简单的说一下我们的搜索引擎的基本原理. 我们给服务器发起请求,例如搜索关键

    2024年01月19日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包