news 2026/7/14 18:48:43

项目:轻量级搜索引擎

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
项目:轻量级搜索引擎

一、项目简介

今天给大家分享我手写的轻量级全文搜索引擎,纯 C++ 开发,无大型中间件依赖,完整复刻商业搜索引擎核心逻辑:

  • HTML 网页数据清洗、结构化解析

  • 手动实现正排索引 + 倒排索引(搜索引擎核心)

  • 结巴中文分词 + 停用词过滤

  • 关键词权重排序、智能摘要生成

  • 基于 httplib 搭建 Web 服务,浏览器直接搜索,返回 JSON 结果

本文拒绝纯空话,全程原理 + 核心代码 + 逐行讲解,适合学习、复盘、简历项目展示。

二、项目整体架构(代码分层)

整个项目分为 4 大代码模块,解耦清晰:

  1. 数据预处理模块:遍历 html、清洗网页、生成 raw.txt 数据源

  2. 通用工具模块 util:文件读取、字符串分割、结巴分词

  3. 索引模块 index:构建正排、倒排索引(项目核心)

  4. 检索 & 服务模块 searcher + main:检索逻辑 + HTTP 在线服务

核心链路:原始HTML → 清洗结构化 → 构建索引 → 分词检索 → 权重排序 → Web接口返回JSON

三、模块一:数据预处理(网页解析)代码详解

目的:本地一堆 Boost 文档 html 文件,无法直接检索,需要批量遍历、去标签、提取标题/内容/URL,生成统一格式的 raw.txt。

1. 批量遍历HTML文件代码

基于 boost::filesystem 递归遍历目录,过滤无效文件:

bool EnumFile(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)){ std::cerr << src_path << " not exists" << std::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; // 只保留html文件 if(iter->path().extension() != ".html") continue; files_list->push_back(iter->path().string()); } return true; }

代码讲解

  • recursive_directory_iterator:递归遍历子目录,适配多层文件结构

  • is_regular_file:过滤文件夹,只保留普通文件

  • 后缀判断:只筛选 html 网页文件,作为数据源

2. 标题解析代码(字符串截取)

static bool ParseTitle(const std::string &file, std::string *title) { std::size_t begin = file.find("<title>"); std::size_t end = file.find("</title>"); if(begin == std::string::npos || end == std::string::npos || begin > end){ return false; } begin += std::string("<title>").size(); *title = file.substr(begin, end - begin); return true; }

原理:通过 find 定位标题标签首尾,substr 截取中间纯文本,简单高效,无需复杂解析库。

3. 正文解析代码(状态机去HTML标签)

核心亮点:手动实现简易状态机,剔除所有 HTML 标签,保留正文纯文本:

static bool ParseContent(const std::string &file, std::string *content) { enum status{ LABLE, CONTENT }; enum status s = LABLE; for( char c : file){ switch(s){ case LABLE: if(c == '>') s = CONTENT; break; case CONTENT: if(c == '<') s = LABLE; else { if(c == '\n') c = ' '; content->push_back(c); } break; default: break; } } return true; }

代码讲解

  • 两种状态:标签内(舍弃)、内容区(保留)

  • 遇到>退出标签,进入内容读取

  • 遇到<进入标签,停止内容读取

  • 自动替换换行符为空格,避免文本格式混乱

4. 结构化保存数据(解决分隔符冲突)

重点踩坑:正文自带换行、逗号,不能用常规符号分割,项目采用不可见ASCII字符 \3分割字段:

#define SEP '\3' for(auto &item : results){ 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'; out.write(out_string.c_str(), out_string.size()); }

四、模块二:通用工具类 util.hpp 代码详解

1. 文件读取工具

static bool ReadFile(const std::string &file_path, std::string *out) { std::ifstream in(file_path, std::ios::in); if(!in.is_open()) return false; std::string line; while(std::getline(in, line)) *out += line; in.close(); return true; }

2. 结巴分词 + 停用词过滤(核心)

搜索引擎不能搜「的、了、啊」这类无意义词汇,这里封装单例分词工具,自动过滤停用词:

void CutStringHelper(const std::string &src, std::vector<std::string> *out) { jieba.CutForSearch(src, *out); // 遍历分词结果,删除停用词 for(auto iter = out->begin(); iter != out->end(); ){ auto it = stop_words.find(*iter); if(it != stop_words.end()) iter = out->erase(iter); else iter++; } }

作用:精简索引、提升检索精准度、减小内存占用。

五、模块三:索引核心 index.hpp(项目灵魂+重点代码)

搜索引擎快的本质:不遍历全文,只查索引。依靠正排+倒排双索引实现毫秒检索。

1. 核心数据结构定义

// 正排索引节点:通过文档ID查文档内容 struct DocInfo{ std::string title; std::string content; std::string url; uint64_t doc_id; }; // 倒排索引节点:通过关键词查文档 struct InvertedElem{ uint64_t doc_id; std::string word; int weight; InvertedElem():weight(0){} }; typedef std::vector<InvertedElem> InvertedList;

2. 线程安全单例索引类

索引全局唯一,禁止多次创建,采用懒汉模式+双重检查锁

static Index* GetInstance() { if(nullptr == instance){ mtx.lock(); if(nullptr == instance){ instance = new Index(); } mtx.unlock(); } return instance; }

3. 倒排索引权重计算核心代码

贴合搜索逻辑:标题权重远大于正文,标题命中的文档排序更靠前

#define X 10 // 标题权重系数 #define Y 1 // 正文权重系数 for(auto &word_pair : word_map){ InvertedElem item; item.doc_id = doc.doc_id; item.word = word_pair.first; // 核心权重算法 item.weight = X*word_pair.second.title_cnt + Y*word_pair.second.content_cnt; inverted_index[word_pair.first].push_back(std::move(item)); }

代码逻辑:同一个词,在标题出现一次=正文出现10次权重,完全贴合用户搜索习惯。

六、模块四:检索器 searcher.hpp 核心代码详解

1. 多关键词检索 + 权重合并

用户输入多个关键词,同一文档命中多个词,权重累加,相关性更高

// 遍历每一个搜索分词 for(std::string word : words){ boost::to_lower(word); ns_index::InvertedList *inverted_list = index->GetInvertedList(word); if(nullptr == inverted_list) continue; // 合并相同文档的权重 for(const auto &elem : *inverted_list){ auto &item = tokens_map[elem.doc_id]; item.doc_id = elem.doc_id; item.weight += elem.weight; item.words.push_back(elem.word); } }

2. 权重降序排序

std::sort(inverted_list_all.begin(), inverted_list_all.end(),\ [](const InvertedElemPrint &e1, const InvertedElemPrint &e2){ return e1.weight > e2.weight; });

权重越高,文档和搜索词越匹配,排序越靠前。

3. 智能摘要生成代码

自动截取关键词前后文本,容错边界,生成搜索摘要:

std::string GetDesc(const std::string &html_content, const std::string &word) { const int prev_step = 50; const int next_step = 100; // 忽略大小写查找关键词 auto iter = std::search( html_content.begin(), html_content.end(), word.begin(), word.end(), [](int x, int y){ return (std::tolower(x) == std::tolower(y)); } ); if(iter == html_content.end()) return "None1"; int pos = std::distance(html_content.begin(), iter); int start = 0, end = html_content.size() - 1; if(pos > start + prev_step) start = pos - prev_step; if(pos < end - next_step) end = pos + next_step; if(start >= end) return "None2"; std::string desc = html_content.substr(start, end - start); desc += "..."; return desc; }

七、模块五:HTTP Web服务 main.cpp 代码详解

基于 cpp-httplib 搭建轻量级服务,无需部署 Nginx,直接浏览器访问搜索:

int main() { // 1. 初始化搜索器 & 索引 ns_searcher::Searcher search; search.InitSearcher(input); // 2. 创建HTTP服务 httplib::Server svr; svr.set_base_dir(root_path.c_str()); // 3. 注册搜索接口 /s svr.Get("/s", [&search](const httplib::Request &req, httplib::Response &rsp) { if (!req.has_param("word")) { rsp.set_content("必须要有搜索关键字!", "text/plain; charset=utf-8"); return; } std::string word = req.get_param_value("word"); std::string json_string; search.Search(word, &json_string); rsp.set_content(json_string, "application/json"); }); LOG(NORMAL, "服务器启动成功..."); svr.listen("0.0.0.0", 8081); return 0; }

接口功能:访问http://127.0.0.1:8081/s?word=xxx即可获取结构化搜索 JSON 结果。

八、核心知识点总结(面试/复盘重点)

1. 正排索引 vs 倒排索引

  • 正排索引:文档ID → 文档信息,用于结果展示、摘要生成

  • 倒排索引:关键词 → 文档列表,检索加速核心,避免遍历全文

2. 权重设计思想

标题关键词权重 > 正文权重,模拟真实搜索引擎相关性排序逻辑。

3. 工程亮点

  • 单例模式全局复用索引、分词器,避免重复IO,提升性能

  • 自定义分隔符解决文本分割冲突问题

  • 停用词过滤、大小写归一化,优化检索效果

  • 轻量Web服务,实现前后端完整交互

九、项目优化方向(拔高亮点)

  • 索引磁盘持久化,无需每次启动重建索引

  • 增加搜索结果分页、关键词高亮

  • 增加缓存,优化高频搜索词响应速度

  • 引入同义词词库,实现智能联想搜索

十、总结

本项目不是简单的 C++ 语法demo,是完整的后端工程实战项目。从数据清洗、索引算法、业务检索到 Web 服务上线,全程手写,覆盖了文件IO、STL、设计模式、算法排序、网络编程等核心知识点。

非常适合写进简历、面试复盘,能清晰讲出索引原理、权重算法、项目踩坑点,吊打大多数初级 C++ 开发者。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 18:47:08

从信息论到损失函数:交叉熵的实战推演

1. 信息论基础&#xff1a;从不确定性到编码长度 要理解交叉熵为何能成为机器学习中的黄金标准&#xff0c;我们需要先回到信息论的起点。想象你正在玩一个20问游戏&#xff0c;对方心里想着一个动物&#xff0c;你每次提问只能得到"是"或"否"的回答。聪明…

作者头像 李华
网站建设 2026/7/14 18:45:59

TI DLP4620S-Q1汽车级DMD芯片:AR-HUD核心引擎深度解析

1. 项目概述&#xff1a;当DLP技术驶入汽车座舱在汽车座舱这个方寸之间&#xff0c;显示技术正经历一场静默的革命。从最初的机械指针仪表&#xff0c;到后来的液晶屏幕&#xff0c;再到如今风头正劲的抬头显示&#xff08;HUD&#xff09;和增强现实抬头显示&#xff08;AR-HU…

作者头像 李华
网站建设 2026/7/14 18:45:19

AD7175-8与PIC18F2553构建高精度信号采集系统

1. 项目概述&#xff1a;高精度信号采集系统的核心价值在工业测量、医疗设备和科研仪器等领域&#xff0c;我们经常需要捕捉微弱的模拟信号并将其转换为数字世界可处理的形态。AD7175-8作为ADI公司推出的高性能ADC&#xff08;模数转换器&#xff09;&#xff0c;配合PIC18F255…

作者头像 李华
网站建设 2026/7/14 18:42:33

都说GEO重要,但外贸网站具体怎么落地?手把手教你

一个外贸主管的实操笔记做了六年外贸网站运营&#xff0c;我听过太多“趋势”了。从移动端适配到语音搜索&#xff0c;从视频营销到AI写文案&#xff0c;每年来一波新概念&#xff0c;每波都要跟&#xff0c;但真正能落地的没几个。去年我开始研究GEO&#xff08;生成式引擎优化…

作者头像 李华
网站建设 2026/7/14 18:40:08

AI特效制作实战:从Stable Diffusion到Runway ML的创意教学应用

最近在技术社区看到不少关于AI辅助创意教学的讨论&#xff0c;特别是特效制作领域。当传统教学遇到AI技术&#xff0c;往往能碰撞出意想不到的火花。本文将通过一个完整的实战案例&#xff0c;展示如何结合AI工具和特效技术&#xff0c;实现"小人族教练体能测试"这样…

作者头像 李华
网站建设 2026/7/14 18:30:28

生产级AI Agent架构设计与工程实践指南

1. 从玩具Demo到生产级AI Agent的进化之路2026年的AI Agent开发领域已经发生了翻天覆地的变化。三年前&#xff0c;我们还在为能跑通一个简单的对话Demo而兴奋不已&#xff1b;如今&#xff0c;企业级AI Agent已经成为数字化转型的核心基础设施。作为一名从2018年就开始接触智能…

作者头像 李华