基于boost准标准库的搜索引擎项目

news2024/11/26 12:16:40

零 项目背景/原理/技术栈

1.介绍boost准标准库

2.项目实现效果

3.搜索引擎宏观架构图

这是一个基于Web的搜索服务架构

该架构优点:

  1. 客户端-服务器模型:采用了经典的客户端-服务器模型,用户通过客户端与服务器交互,有助于集中管理和分散计算。
  2. 简单的用户界面:客户端似乎很简洁,用户通过简单的HTTP请求与服务端交互,易于用户操作。
  3. 搜索引擎功能:服务器端的搜索器能够接收查询请求,从数据存储中检索信息,这是Web搜索服务的核心功能。
  4. 数据存储:有专门的存储系统用于存放数据文件(如HTML文件),有助于维护数据的完整性和持久性。
  5. 模块分离:搜索器、存储和处理请求的模块被分开,这有助于各模块独立更新和维护.

该架构不足:

  1. 单一服务器瓶颈:所有请求似乎都经过一个中心服务器处理,这可能会导致瓶颈,影响扩展性和可用性。
  2. 缺乏负载均衡:在架构图中没有显示负载均衡系统,当大量并发请求到来时可能会影响性能。
  3. 没有明确的缓存策略:对于频繁搜索的内容,缓存可以显著提高响应速度,降低服务器压力,架构图中没有体现出缓存机制。
  4. 可靠性和冗余性:没有看到备份服务器或数据复制机制,这对于数据的安全性和服务的持续可用性非常重要。
  5. 安全性:架构图中未展示任何安全措施,例如SSL加密通信、防火墙、入侵检测系统等。

4.搜索过程的原理~正排,倒排索引

5.技术栈和项目环境,工具

技术栈:C/C++ C++11 STL boost准标准库 JsonCPP cppjieba cpp-httplib 
html css js jQuery Ajax

项目环境:Centos7  华为云服务器 gcc/g++/makefile Vscode

一 Paser数据清洗,获取数据源模块


const std::string src_path = "data/input/";
const std::string output_file = "data/output/dest.txt";
class DocInfo
{
public:
    std::string _title;
    std::string _content;
    std::string _url;
};

Paser模块主逻辑 

int main()
{
    std::vector<std::string> files_list;
    // 第一步 把搜索范围src_path内的所有html的路径+文件名放到 files_list中
    if (!EnumFileName(src_path, &files_list))
    {
        lg(_Error,"%s","enum filename err!");
        exit(EnumFileNameErr);
    }

    // 第二步 将files_list中的文件打开,读取并解析为DocInfo后放到 web_documents中
    std::vector<DocInfo> html_documents;
    if (!ParseHtml(files_list, &html_documents))
    {
        lg(_Error,"%s","parse html err!");
        exit(ParseHtmlErr);
    }

    // 第三步 将web_documents的信息写入到 output_file文件中, 以\3为每个文档的分隔符
    if (!SaveHtml(html_documents, output_file))
    {
        lg(_Error,"%s","save html err!");
        exit(SaveHtmlErr);
    }
}
  1. 枚举文件:从给定的源路径(src_path)中枚举所有HTML文件,并将它们的路径和文件名放入files_list中。

  2. 解析HTML:读取files_list中的每个文件,解析它们为DocInfo对象(可能包含标题、URL、正文等元素),然后存储到html_documents向量中。

  3. 保存文档:将html_documents中的文档信息写入到指定的输出文件output_file中,文档之间用\3(ASCII码中的End-of-Text字符)分隔。

EnumFileName

bool EnumFileName(const std::string &src_path, std::vector<std::string> *files_list)
{
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);
    if (!fs::exists(root_path)) // 判断路径是否存在
    {
        lg(_Fatal,"%s%s",src_path.c_str()," is not exist");
        return false;
    }

    // 定义一个空迭代器,用来判断递归是否结束
    fs::recursive_directory_iterator end;
    // 递归式遍历文件
    for (fs::recursive_directory_iterator it(src_path); it != end; it++)
    {
        if (!fs::is_regular(*it))
            continue; // 保证是普通文件
        if (it->path().extension() != ".html")
            continue; // 保证是.html文件

        files_list->push_back(it->path().string()); // 插入的都是合法 路径+.html文件名
    }

    return true;
}

ParseHtml

bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo> *html_documents)
{
    for (const std::string &html_file_path : files_list)
    {
        // 第一步 遍历files_list,根据路径+文件名,读取html文件内容
        std::string html_file;
        if (!ns_util::FileUtil::ReadFile(html_file_path, &html_file))
        {
            lg(_Error,"%s","ReadFile err!");
            continue;
        }
        DocInfo doc_info;
        // 第二步 解析html文件,提取title
        if (!ParseTitle(html_file, &doc_info._title))
        {
            lg(_Error,"%s%s","ParseTitle err! ",html_file_path.c_str());
            continue;
        }
        // 第三步 解析html文件,提取content(去标签)
        if (!ParseContent(html_file, &doc_info._content))
        {
            lg(_Error,"%s","ParseContent err!");
            continue;
        }
        // 第四步 解析html文件,构建url
        if (!ParseUrl(html_file_path, &doc_info._url))
        {
            lg(_Error,"%s","ParseUrl err!");
            continue;
        }

        // 解析html文件完毕,结果都保存到了doc_info中
        // ShowDcoinfo(doc_info);
        html_documents->push_back(std::move(doc_info)); // 尾插会拷贝,效率不高,使用move
    }
    lg(_Info,"%s","ParseHtml success!");
    return true;
}

1.ReadFile

    class FileUtil
    {
    public:
        static bool ReadFile(const std::string &file_path, std::string *out)
        {
            std::ifstream in(file_path, std::ios::in); // 以输入方式打开文件
            if (!in.is_open())
            {
                lg(_Fatal,"%s%s%s","ReadFile:",file_path.c_str()," open err!");
                return false;
            }

            std::string line;
            while (std::getline(in, line))
            {
                *out += line;
            }
            in.close();

            return true;
        }
    };

2.ParseTitle

static bool ParseTitle(const std::string &html_file, std::string *title)
{
    size_t left = html_file.find("<title>");
    if (left == std::string::npos)
        return false;
    size_t right = html_file.find("</title>");
    if (right == std::string::npos)
        return false;

    int begin = left + std::string("<title>").size();
    int end = right;
    // 截取[begin,end-1]内的子串就是标题内容
    if (end-begin<0)
    {
        lg(_Error,"%s%s%s","ParseTitle:",output_file.c_str(),"has no title");
        //std::cerr << "ParseTitle:" << output_file << "has no title" << std::endl;
        return false;
    }

    std::string str = html_file.substr(begin, end - begin);
    //std::cout << "get a title: " << str << std::endl;
    *title = str;
    return true;
}

3.ParseContent

static bool ParseContent(const std::string &html_file, std::string *content)
{
    // 利用简单状态机完成去标签工作
    enum Status
    {
        Lable,
        Content
    };

    Status status = Lable;
    for (char ch : html_file)
    {
        switch (status)
        {
        case Lable:
            if (ch == '>')
                status = Content;
            break;
        case Content:
            if (ch == '<')
                status = Lable;
            else
            {
                // 不保留html文本中自带的\n,防止后续发生冲突
                if (ch == '\n')
                    ch = ' ';
                content->push_back(ch);
            }
            break;
        default:
            break;
        }
    }

    return true;
}

4.ParseUrl

static bool ParseUrl(const std::string &html_file_path, std::string *url)
{
    std::string url_head = "https://www.boost.org/doc/libs/1_84_0/doc/html";
    std::string url_tail = html_file_path.substr(src_path.size());

    *url = url_head + "/" + url_tail;
    return true;
}

SaveHtml

//doc_info内部用\3分隔,doc_info之间用\n分隔
bool SaveHtml(const std::vector<DocInfo> &html_documents, const std::string &output_file)
{
    const char sep = '\3';
    std::ofstream out(output_file, std::ios::out | std::ios::binary|std::ios::trunc);
    if (!out.is_open())
    {
        lg(_Fatal,"%s%s%s","SaveHtml:",output_file.c_str()," open err!");
        return false;
    }

    for(auto &doc_info:html_documents)
    {
        std::string outstr;
        outstr += doc_info._title;
        outstr += sep;
        outstr += doc_info._content;
        outstr += sep;
        outstr+= doc_info._url;
        outstr+='\n';

        out.write(outstr.c_str(),outstr.size());
    }
    out.close();
    lg(_Info,"%s","SaveHtml success!");
    return true;
}

二 Index建立索引模块

三 Searcher搜索模块

四 http_server模块

const std::string input = "data/output/dest.txt";//从input里读取数据构建索引
const std::string root_path = "./wwwroot";
int main()
{
        std::unique_ptr<ns_searcher::Searcher> searcher(new ns_searcher::Searcher());
        searcher->SearcherInit(input);
        httplib::Server svr;
        svr.set_base_dir(root_path.c_str()); // 设置根目录
                                             // 重定向到首页
        svr.Get("/", [](const httplib::Request &, httplib::Response &rsp)
                { rsp.set_redirect("/home/LZF/boost_searcher_project/wwwroot/index.html"); }); // 重定向到首页
        svr.Get("/s",[&searcher](const httplib::Request &req,httplib::Response &rsp)
        {
                if(!req.has_param("word"))
                {
                        rsp.set_content("无搜索关键字!","test/plain,charset=utf-8");
                        return;
                }
                std::string json_str;
                std::string query = req.get_param_value("word");
                std::cout<<"用户正在搜索: "<<query<<std::endl;
                searcher->Search(query,&json_str);
                rsp.set_content(json_str,"application/json");
        });

        svr.listen("0.0.0.0", 8800);
}

五 前端模块

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>

    <title>boost 搜索引擎</title>
    <style>
        /* 去掉网页中的所有的默认内外边距,html的盒子模型 */
        * {
            /* 设置外边距 */
            margin: 0;
            /* 设置内边距 */
            padding: 0;
        }
        /* 将我们的body内的内容100%和html的呈现吻合 */
        html,
        body {
            height: 100%;
        }
        /* 类选择器.container */
        .container {
            /* 设置div的宽度 */
            width: 800px;
            /* 通过设置外边距达到居中对齐的目的 */
            margin: 0px auto;
            /* 设置外边距的上边距,保持元素和网页的上部距离 */
            margin-top: 15px;
        }
        /* 复合选择器,选中container 下的 search */
        .container .search {
            /* 宽度与父标签保持一致 */
            width: 100%;
            /* 高度设置为52px */
            height: 52px;
        }
        /* 先选中input标签, 直接设置标签的属性,先要选中, input:标签选择器*/
        /* input在进行高度设置的时候,没有考虑边框的问题 */
        .container .search input {
            /* 设置left浮动 */
            float: left;
            width: 600px;
            height: 50px;
            /* 设置边框属性:边框的宽度,样式,颜色 */
            border: 1px solid black;
            /* 去掉input输入框的有边框 */
            border-right: none;
            /* 设置内边距,默认文字不要和左侧边框紧挨着 */
            padding-left: 10px;
            /* 设置input内部的字体的颜色和样式 */
            color: #CCC;
            font-size: 14px;
        }
        /* 先选中button标签, 直接设置标签的属性,先要选中, button:标签选择器*/
        .container .search button {
            /* 设置left浮动 */
            float: left;
            width: 150px;
            height: 52px;
            /* 设置button的背景颜色,#4e6ef2 */
            background-color: #4e6ef2;
            /* 设置button中的字体颜色 */
            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;
            /* a标签的下划线去掉 */
            text-decoration: none;
            /* 设置a标签中的文字的字体大小 */
            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>
<body>
    <div class="container">
        <div class="search">
            <input type="text" value="请输入搜索关键字">
            <button onclick="Search()">搜索一下</button>
        </div>
        <div class="result">
        </div>
    </div>
    <script>
        function Search(){
            let query = $(".container .search input").val();
            console.log("query = " + query);
    
            $.get("/s", {word: query}, function(data){
                console.log(data);
                BuildHtml(data);
            });
        }
    
        function BuildHtml(data){
            let result_lable = $(".container .result");
            result_lable.empty();
    
            for( let elem of data){
                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>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1557269.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DBeaver,一款实用的开源数据库管理软件

说起开源软件&#xff0c;其实大部分的体验和服务都是没有商业软件好的&#xff0c;毕竟养团队不是靠鼓励和奉献&#xff0c;咱们选择开源软件的主要原因还是免费&#xff0c;免费&#xff0c;免费。 由于公司限制安装商业软件&#xff0c;咱只能挑开源的替代&#xff0c;其中…

HarmonyOS 应用开发之Stage模型启动FA模型PageAbility

本小节介绍Stage模型的两种应用组件如何启动FA模型的PageAbility组件。 UIAbility启动PageAbility UIAbility启动PageAbility和UIAbility启动UIAbility的方式完全相同。 说明&#xff1a; 需注意FA模型中abilityName由bundleName AbilityName组成&#xff0c;具体见示例。 i…

设计模式之解释器模式的魅力:让代码读懂你的语言

目录 一、什么是解释器模式 二、解释器模式的应用场景 三、解释器模式的优缺点 3.1. 优点 3.2. 缺点 四、解释器模式示例 4.1. 问题描述 4.2. 问题分析 4.3. 代码实现 4.4. 优化方向 五、总结 一、什么是解释器模式 解释器模式&#xff08;Interpreter pattern&…

AI图像超分解决方案,还原更加清晰、逼真的画面

图像质量对于企业的业务运营和用户体验至关重要&#xff0c;许多场景下的图像分辨率不足&#xff0c;常常导致模糊效果不佳&#xff0c;难以满足企业的视觉需求。针对这一难题&#xff0c;美摄科技凭借其领先的AI技术&#xff0c;推出了创新的图像超分解决方案&#xff0c;为企…

【编程笔记】学会使用 Git

目录 一、介绍 Git二、安装 Git三、 常用 linux 目录四、Git 的必要配置(1) 查看和删除之前的配置(2) 配置 Git 五、Git 基本理论六、Git 项目搭建七、Git 文件操作八、分支Git 笔记 ❀❀❀(1) 常规使用(2) 分支 一、介绍 Git &#x1f4d6; VCS&#xff1a;Version Control S…

代码随想录-二叉树(路径)

目录 257. 二叉树的所有路径 题目描述&#xff1a; 输入输出描述&#xff1a; 思路和想法&#xff1a; 404. 左叶子之和 题目描述&#xff1a; 输入输出描述&#xff1a; 思路和想法&#xff1a; 513.找树左下角的值 题目描述&#xff1a; 输入输出描述&#xff1a;…

用JSch实现远程传输文件并打包成jar

本文将简单介绍一下 JSch 这个Java的第三方库的一个简单用法&#xff0c;并以此为实例&#xff0c;讲解 IntelliJ 中打包成 jar 包的2种方式。 实现目标 我们的目标是&#xff0c;做出一个jar包&#xff0c;它能够实现类似于 scp 命令的远程传输文件的功能。用法如下&#xf…

乡村数字化转型:科技赋能打造智慧农村新生态

随着信息技术的迅猛发展&#xff0c;数字化转型已成为推动社会进步的重要引擎。在乡村振兴的大背景下&#xff0c;乡村数字化转型不仅是提升乡村治理能力和治理水平现代化的关键&#xff0c;更是推动农业现代化、农村繁荣和农民增收的重要途径。本文旨在探讨乡村数字化转型的内…

MyBatis 初识简单操作

前言 上一期我们讲完Spring的配置文件以及日志的设置,这一期我们就来谈谈mybatis操作数据库的一些操作,使用这个框架可以极大地简化JDBC的冗长代码,大大增强了生产力,只需我们提供简单的sql语句以及对应的注解就可以操作数据库 我们说web应用程序主要分为三层 Controller Serv…

使用 golang 以及 Gin 框架,将上传的图片在不保存至本地的情况下添加水印,并上传至阿里云 OSS

正如标题所述&#xff0c;使用golang对上传图片添加水印&#xff0c;以及将图片上传到阿里云OSS&#xff0c;网上一搜索&#xff0c;便有你想要的结果了&#xff0c;可是&#xff0c;他们却先将上传图片添加水印后保存在本地&#xff0c;而后再将添加了水印的图片上传到阿里云O…

SRS OBS利用RTMP协议实现音视频推拉流

参考&#xff1a;https://ossrs.net/lts/zh-cn/docs/v5/doc/getting-started 1&#xff09;docker直接运行SRS服务&#xff1a; docker run --rm -it -p 1935:1935 -p 1985:1985 -p 8080:8080 registry.cn-hangzhou.aliyuncs.com/ossrs/srs:5运行起来后可以http://localho…

学习笔记——C语言基本概念指针(上)——(7)

今天学习了指针&#xff0c;指针吧理解有点小难&#xff0c;慢慢分析就懂。 在开始学指针之前先回顾一下C语言的数据类型如下图所示: 按照分类分别为&#xff1a; 1->基础数据类型&#xff1a;char &#xff1b;short&#xff1b; int&#xff1b; long&#xff1b; float&…

Platypus 一种集中式的央行数字货币方案

集中式的CBDC&#xff0c;混合使用账户模型和UTXO模型。 角色分类 中央银行&#xff1a;发行货币&#xff0c;交易验证&#xff0c;公开交易日志&#xff0c;防止双花。 不是完全受信任的&#xff0c;假定为会遵守监管要求&#xff0c;但可能会破坏交易隐私&#xff0c;即获…

瑞吉外卖实战学习--5、新增员工功能

新增员工功能 效果图1、开发流程2、页面发送ajax请求,将新增员工的信息以json的形式提交给服务器2.1、在填写信息的时候会发现身份校验比较麻烦,可以在validate中将全局的校验方式去掉,方便填写2.3、看到接口未employee2.4、前端代码分析3、服务器接收到提交的数据并调用ser…

无论PC还是Mac,都能畅快地使用移动硬盘 Mac使用NTFS移动硬盘不能读写

如果你拥有一台Mac设备&#xff0c;总会遇到尴尬的那一刻——你在Mac上用得好好的移动硬盘怎么都不能被PC识别到。又或者你朋友在PC上用得好好的移动硬盘&#xff0c;连上你的Mac后&#xff0c;Mac里的文件死活就是拷贝不进移动硬盘里。这种坑&#xff0c;相信大多数使用Mac的小…

Linux 基于chrony进行时钟同步方案验证

Linux 基于chrony进行时钟同步方案验证 1. 背景介绍2. 验证过程2.1 追踪配置2.2 追平记录2.2 追平时间换算 3. 疑问和思考3.1 如何统计追踪1s需要花费多长时间&#xff1f; 4. 参考文档 chrony是一个Linux系统中用于时钟同步的工具。它使用NTP&#xff08;网络时间协议&#xf…

【Java常用的API】JDK8相关时间类

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

Delphi 12 安卓 部署文件,不支持中文文件名

procedure TForm3.Button1Click(Sender: TObject); var sFileName:string; begin sFileName:TPath.Combine(TPath.GetDocumentsPath,禁止吸烟.wav); showmessage(sFileName); MediaPlayer1.Stop ; MediaPlayer1.FileName: sFileName; MediaPlayer1.Play; end;

c语言:vs2022写一个一元二次方程(包含虚根)

求一元二次方程 的根&#xff0c;通过键盘输入a、b、c&#xff0c;根据△的值输出对应x1和x2的值(保留一位小数)(用if语句完成)。 //一元二次方程的实现 #include <stdio.h> #include <math.h> #include <stdlib.h> int main() {double a, b, c, delta, x1…

商品说明书的制作工具来啦,用这几个就够了!

商品说明书是用户了解产品特性、性能、使用方法的重要途径。一个明确、易懂的商品说明书&#xff0c;可以显著提升用户体验&#xff0c;进而提升产品的销量。但我们都知道&#xff0c;制作一份高质量的说明书并不容易&#xff0c;需要仔细设计、计划和撰写。幸运的是&#xff0…