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

【项目实践】boost 搜索引擎

1. 项目展示

boost搜索引擎具体讲解视频

2. 项目背景

对于boost库,官方是没有提供搜索功能的,我们这个项目就是来为它添加一个站内搜索的功能。

3. 项目环境与技术栈

项目环境: ubuntu22.04、vscode
技术栈: C/C++、C++11、STL、Boost、Jsoncpp、cppjieba、cpp-httplib、html5、css、js、jQuery、Ajax

4. 搜索引擎的宏观原理

后端: 首先通过爬虫程序将全网中的数据保存到磁盘中,接下来通过去标签和数据清洗得到我们想要的数据格式,接下来为这些数据建立好索引方便搜索引擎检索。
前端: 用户通过浏览器通过GET方式上传关键字,通过http请求搜索引擎提供服务,搜索引擎检索相关的数据并动态构建网页返回用户。
在这里插入图片描述

5. 数据获取

我们这里就不采用爬虫的方式获取boost库中的内容了,因为官方已经提供了下载的途径:
在这里插入图片描述
我们实际需要的只是boost_1_88_0/doc/html 这里面的内容,我们将其拷贝到我们的data/raw_input目录中方便后续使用。

6. 去标签与数据清洗

我们浏览data/raw_input其中的html文件发现其中都包含着很多的标签:
在这里插入图片描述
而这些标签中的内容对于我们来说是没有价值的,因此我们需要去除这些标签,并把处理好的数据放在data/input中。

6.1 处理策略

在我们搜索网页时可以看到其显示的内容主要分为三部分:
在这里插入图片描述
因此我们在数据清洗时也要体现出这三部分数据:将每个html文件中的内容读取成一行以\n结尾,每一行中分为三个部分(title、content、url)以\3分隔。

6.2 基本框架

#include <memory>
#include "Parse.hpp"using namespace ParseModule;int main()
{std::unique_ptr<Parse> parser = std::make_unique<Parse>();// 1. 枚举所有的.html文件if(! parser->EnumFileName()){LOG(LogLevel::FATAL) << "EnumFileName Failed";exit(1);}// 2. 将所有的文件名对应的文件转换成指定格式的数组if(!parser->ParseHtml()){LOG(LogLevel::FATAL) << "ParseHtml Failed";exit(2);}// 3. 将数组中的内容拼接成json字符串并保存到input_path中if(!parser->SaveHtml()){LOG(LogLevel::FATAL) << "SaveHtml Failed";exit(3);}LOG(LogLevel::DEBUG) << "Parse Succeed!";return 0;
}

6.3 分步实现

• 我们需要将 data/raw_input/ 下的所有 .html 文件的名字获取得到,boost库中为我们提供了相应的方法。因此我们需要引入boost库,使用boost库中的filesystem帮助我们遍历给定路径的文件,来帮我们筛选出.html的文件。
• 获取得到所有的.html文件之后,需要提取出我们想要的内容(title、content、url),所以要依次遍历所有的文件,提取出这三部分。
• 在获取到我们想要的内容之后,我们需要将这些数据以一定的格式进行保存,这里我们采用的是每个文件的内容放在一行,行内使用'\3'进行分隔,将其存储到data/input/input.bin中。
boost 库的下载

sudo apt update
sudo apt install -y libboost-all-dev

具体实现

#pragma once
#include <fstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <boost/filesystem.hpp>
#include "Log.hpp"namespace ParseModule
{using namespace LogModule;const static std::string raw_input_path = "../data/raw_input";const static std::string input_path = "../data/input/input.bin";struct DataInfo{std::string title;   // 标题std::string content; // 正文std::string url;     // url};using DataInfo_t = struct DataInfo;class Parse{public:Parse(){}// 枚举所有的html文件bool EnumFileName(){boost::filesystem::path root_path(raw_input_path);// 如果节点不存在就返回 falseif (!boost::filesystem::exists(root_path))return false;// 遍历所有的文件boost::filesystem::recursive_directory_iterator end;boost::filesystem::recursive_directory_iterator iter(root_path);for (; iter != end; iter++){// 判断是不是常规文件if (!boost::filesystem::is_regular_file(*iter))continue;// 判断是不是.html文件if (iter->path().extension() != std::string(".html"))continue;// 走到这里一定是一个.html文件_files_name.push_back(move(iter->path().string()));}return true;}// 对文件中的内容进行划分bool ParseHtml(){for (auto &file_name : _files_name){// 读取文件内容std::string message;if (!ReadFile(file_name, &message)){LOG(LogLevel::FATAL) << "ReadFile Failed";return false;}// 构建DataInfoDataInfo_t datainfo;if (!BuiltDataInfo(file_name, message, &datainfo)){LOG(LogLevel::FATAL) << "BuiltDataInfo Failed";return false;}// 将构建成功的datainfo插入datas_datas.push_back(datainfo);}return true;}// 将指定格式的数据写入指定文件bool SaveHtml(){// 按照二进制方式进行写入std::ofstream out(input_path, std::ios::out | std::ios::binary);if (!out.is_open()){std::cerr << "open " << input_path << " failed!" << std::endl;return false;}const static std::string sep = "\3";for (auto &data : _datas){std::string outstr;outstr += data.title + sep;outstr += data.content + sep;outstr += data.url + '\n';out.write(outstr.c_str(), outstr.size());}out.close();return true;}~Parse(){}private:bool ReadFile(const std::string &file_name, std::string *result){std::ifstream in(file_name, std::ios::in);if (!in.is_open()){LOG(LogLevel::ERROR) << "open file " << file_name << " error";return false;}std::string line;while (std::getline(in, line))*result += line;in.close();return true;}bool BuiltDataInfoTitle(std::string &message, std::string *title){size_t begin = message.find("<title>");if (begin == std::string::npos)return false;size_t end = message.find("</title>");if (end == std::string::npos)return false;begin += std::string("<title>").size();*title = message.substr(begin, end - begin);return true;}bool BuiltDataInfoContent(std::string &message, std::string *content){size_t begin = message.find("<body");if (begin == std::string::npos)return false;size_t end = message.find("</body>");if (end == std::string::npos)return false;begin += std::string("<body>").size();// 基于一个简易的状态机去标签enum status{LABLE,CONTENT};enum status s = LABLE;while (begin != end){switch (s){case LABLE:if (message[begin] == '>')s = CONTENT;break;case CONTENT:if (message[begin] == '<')s = LABLE;else{// 我们不想保留原始文件中的\n,因为我们想用\n作为html解析之后文本的分隔符if (message[begin] == '\n')message[begin] = ' ';content->push_back(message[begin]);}break;default:break;}begin++;}return true;}bool BuiltDataInfoUrl(std::string &file_name, std::string *url){std::string url_head = "https://www.boost.org/doc/libs/1_88_0/doc/html";std::string url_tail = file_name.substr(raw_input_path.size());*url = url_head + url_tail;return true;}bool BuiltDataInfo(std::string &filename, std::string &message, DataInfo_t *datainfo){// 构建titleif (!BuiltDataInfoTitle(message, &datainfo->title))return false;// 构建contentif (!BuiltDataInfoContent(message, &datainfo->content))return false;// 构建urlif(!BuiltDataInfoUrl(filename,&datainfo->url))return false;return true;}private:std::vector<std::string> _files_name; // 1. 将raw中的html文件名全部保存到files_name中std::vector<DataInfo_t> _datas;       // 2. 将所有的文件名对应的文件转换成指定格式的数组};
}

7. 建立索引

7.1 正排索引与倒排索引概述

正排索引: 从文档ID找到文档内容(文档内的关键字)

文档ID文档内容
1caryon爱在CSDN写博客
2CADN上有好多优质博客

倒排索引: 根据文档内容对应联系到文档ID

关键字文档ID
caryon1
CSDN1、2
写博客1
博客1、2
优质博客2

7.2 基本框架

#pragma oncenamespace IndexModule
{// 正排索引元素typedef struct ForwardElem{std::string title;   // titlestd::string content; // contentstd::string url;     // urlint data_id;         // id} ForwardElem;// 倒排索引元素typedef struct InvertedElem{int data_id;          // data_idstd::string key_word; // key_wordlong long weight;     // weight} InvertedElem;// 倒排链表using InvertedList = std::vector<InvertedElem>;class Index{public:Index() {}// 获取正排索引对应的元素ForwardElem *GetForwardElem(int data_id){}// 获取倒排索引对应的元素InvertedList *GetInvertedList(const std::string &word){}// 构建索引bool BuiltIndex(const std::string &input_path){// 构建正排索引// 构建倒排索引}~Index() {}private:Index* instance;};
}

7.3 分步实现

正排索引实际上就是对data/input/input.bin中的内容进行读取并按照一定的格式进行创建,它的标号天然就存在了(数组下标)。倒排索引的话就需要将获取的正排索引的元素拆分成若干词(这个工作我们交由jieba来做),而后将这些词与编号一一对应起来,这里有一点很重要,查阅到的文档内容我们按照什么样的顺序进行展示呢?这里我们采用了一定的相关性进行绑定的。
至于返回正排索引和倒排索引对应的元素只需要查找一下即可。
还有一点就是,我们实际上的索引只需要建立一次就可以了,因此可以设置为单例模式。
jieba库的下载
本次使用的jieba我们从git code获取,我是将它保存到了libs目录下的,需要注意的是要将dsps/limonp拷贝到include下才能正确使用,或者建立软连接也可以。

git clone https://gitee.com/mohatarem/cppjieba.git

具体实现

#pragma once
#include <mutex>
#include <fstream>
#include <vector>
#include <string>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
#include "Log.hpp"
#include "Jieba.hpp"
namespace IndexModule
{using namespace LogModule;// 正排索引元素typedef struct ForwardElem{std::string title;   // titlestd::string content; // contentstd::string url;     // urlint data_id;         // id} ForwardElem;// 倒排索引元素typedef struct InvertedElem{int data_id;          // data_idstd::string key_word; // key_wordlong long weight;     // weight// 这个函数是给search.hpp去重使用的bool operator==(const InvertedElem& e){return data_id == e.data_id && key_word == e.key_word && weight == e.weight;}} InvertedElem;// 倒排链表using InvertedList = std::vector<InvertedElem>;class Index{Index() {}Index(const Index&) = delete;bool operator=(const Index&) = delete;static Index* instance;static std::mutex lock;public:static Index* GetInstance(){if(instance == nullptr){std::lock_guard<std::mutex> lck (lock);if(instance == nullptr)instance = new(Index);}return instance; }// 获取正排索引对应的元素ForwardElem *GetForwardElem(int data_id){if (data_id > ForwardIndex.size())return nullptr;return &ForwardIndex[data_id];}// 获取倒排索引对应的元素InvertedList *GetInvertedList(const std::string &word){auto it = InvertedIndex.find(word);if (it == InvertedIndex.end())return nullptr;return &InvertedIndex[word];}// 构建索引bool BuiltIndex(const std::string &input_path){std::ifstream in(input_path, std::ios::in | std::ios::binary);if (!in.is_open()){LOG(LogLevel::FATAL) << "sorry, " << input_path << " open error";return false;}std::string line;int cnt = 0;while (getline(in, line)){// 构建正排索引ForwardElem *forward_elem = BuiltForwardIndex(line);if (forward_elem == nullptr)continue;// 构建倒排索引if (!BuiltInvertedIndex(*forward_elem))continue;cnt++;if(cnt % 50 == 0)LOG(LogLevel::DEBUG) << "已经建立连接:" << cnt ;}return true;}~Index() {}private:ForwardElem *BuiltForwardIndex(const std::string &line){// 1. 解析字符串进行切割std::vector<std::string> part_elem;const static std::string sep = "\3";boost::split(part_elem, line, boost::is_any_of(sep), boost::token_compress_on);if (part_elem.size() != 3)return nullptr;// 2. 将其填充到ForwardElem结构ForwardElem forward_elem;forward_elem.title = part_elem[0];forward_elem.content = part_elem[1];forward_elem.url = part_elem[2];forward_elem.data_id = ForwardIndex.size();// 3. 将构造好的ForwardElem结构插入ForwardIndexForwardIndex.push_back(std::move(forward_elem));return &ForwardIndex.back();}bool BuiltInvertedIndex(ForwardElem &forward_elem){// 统计词频,用于weight的构造struct word_cnt{int title_cnt;int content_cnt;word_cnt() : title_cnt(0), content_cnt(0) {}};// 用来暂存词频的映射表std::unordered_map<std::string, word_cnt> word_map;// 对title进行切分并统计std::vector<std::string> title_key_words;JiebaUtil::CutString(forward_elem.title, &title_key_words);for (auto &key_word : title_key_words){// 忽略大小写boost::to_lower(key_word);word_map[key_word].title_cnt++;}// 对content进行切分并统计std::vector<std::string> content_key_words;JiebaUtil::CutString(forward_elem.content, &content_key_words);for (auto &key_word : content_key_words){boost::to_lower(key_word);word_map[key_word].content_cnt++;}// 将关键字依次插入InvertedIndexfor(auto& key_word:word_map){InvertedElem elem;elem.data_id = forward_elem.data_id;elem.key_word = key_word.first;elem.weight = 10 * key_word.second.title_cnt + key_word.second.content_cnt; // 这里的weight构造采用了硬编码InvertedIndex[key_word.first].push_back(std::move(elem));}return true;}private:std::vector<ForwardElem> ForwardIndex;                       // 正排索引std::unordered_map<std::string, InvertedList> InvertedIndex; // 倒排索引};Index* Index::instance = nullptr;std::mutex Index::lock;
}

8. 搜索引擎

8.1 基本框架

#pragma oncenamespace SearchModule
{using namespace IndexModule;class Search{public:Search(){}// 初始化搜索引擎void InitSearch(const std::string &bin_path){}// 对查询做出反馈std::string Searcher(std::string query) // 这里是故意写成拷贝的{// 1. 对 query 进行切分// 2. 将所有的关键字构成的 InvertedElem 进行保存// 3. 按weight降序排序并去重// 4. 将所有的结果按json串的格式返回}~Search(){}private:Index *index;};
}

8.2 分步实现

搜索引擎是本博客的核心内容了,但是经过前面的处理,这里我们需要做的就只有初始化引擎和对用户的查询做出反馈,这里我们采用json串进行返回是为了方便后续的网络服务。
jsoncpp的安装

sudo apt install  -y libjsoncpp-dev

具体实现

#pragma once
#include <string>
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <jsoncpp/json/json.h>
#include "Index.hpp"
#include "Jieba.hpp"
#include "Log.hpp"namespace SearchModule
{using namespace IndexModule;using namespace LogModule;class Search{public:Search(){}// 初始化搜索引擎void InitSearch(const std::string &bin_path){index = Index::GetInstance();LOG(LogLevel::INFO) << "获取单例成功……";index->BuiltIndex(bin_path);LOG(LogLevel::INFO) << "建立索引成功";}// 对查询做出反馈std::string Searcher(std::string query) // 这里是故意写成拷贝的{// 忽略大小写boost::to_lower(query);// 1. 对 query 进行切分std::vector<std::string> key_words;JiebaUtil::CutString(query, &key_words);// 2. 将所有的关键字构成的 InvertedElem 进行保存InvertedList invertedlist_all;for (const auto &key_word : key_words){InvertedList *invertedlist = index->GetInvertedList(key_word);if (invertedlist == nullptr)continue;invertedlist_all.insert(invertedlist_all.end(), invertedlist->begin(), invertedlist->end());}// 3. 按weight降序排序并去重std::sort(invertedlist_all.begin(), invertedlist_all.end(), [](const InvertedElem &e1, const InvertedElem &e2){ return e1.weight > e2.weight; });auto last = std::unique(invertedlist_all.begin(), invertedlist_all.end());invertedlist_all.erase(last, invertedlist_all.end());// 4. 将所有的结果按json串的格式返回Json::Value root;for (auto &invertedlist : invertedlist_all){ForwardElem *forwardelem = index->GetForwardElem(invertedlist.data_id);if (forwardelem == nullptr){continue;}Json::Value elem;elem["title"] = forwardelem->title;// content是文档的去标签的结果,但是不是我们想要的,我们要的是一部分elem["desc"] = GetDesc(forwardelem->content, invertedlist.key_word);elem["url"] = forwardelem->url;root.append(elem);}return Json::StyledWriter().write(root);}~Search(){}private:std::string GetDesc(const std::string &content, const std::string &key_word){// 找到word在html_content中的首次出现,然后往前找50字节(如果没有,从begin开始),往后找100字节(如果没有,到end就可以的)const int prev_step = 50;const int next_step = 100;// 1. 找到首次出现auto iter = std::search(content.begin(), content.end(), key_word.begin(), key_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);// 2. 获取start,end int start = 0;int end = content.size() - 1;// 如果之前有50+字符,就更新开始位置if (pos > start + prev_step)start = pos - prev_step;if (pos < end - next_step)end = pos + next_step;// 3. 截取子串,returnif (start >= end)return "None2";std::string desc = content.substr(start, end - start);desc += "...";return desc;}private:Index *index;};
}

9. 网络服务

网络服务这里我们采用cpp-httplib库来实现
cpp-httplib 安装

git clone https://gitee.com/welldonexing/cpp-httplib.git

具体实现

#pragma once
#include <memory>
#include "Search.hpp"
#include "../libs/cpp-httplib/httplib.h"namespace HttpSeverModule
{using namespace SearchModule;const std::string rootpath = "../html";class HttpSever{public:HttpSever() : _searcher(std::make_unique<Search>()){}void Start(const std::string &bin_path){_searcher->InitSearch(bin_path);_svr.set_base_dir(rootpath.c_str());_svr.Get("/s", [&](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");LOG(LogLevel::INFO) << "用户搜索的: " << word;std::string json_string = _searcher->Searcher(word);rsp.set_content(json_string, "application/json");});LOG(LogLevel::INFO) << "服务器启动成功...";_svr.listen("0.0.0.0", 8888);}~HttpSever(){}private:std::unique_ptr<Search> _searcher;httplib::Server _svr;};
}

10. 前端界面

这一部分内容只要自己能够实现一个搜索功能即可,谨放上我的代码供大家查看

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Boost 搜索引擎</title><style>/* 可复用 Google 风格样式 */body {display: flex;justify-content: center;align-items: center;flex-direction: column;height: 100vh;font-family: Arial, sans-serif;}.logo {font-size: 64px;font-weight: bold;color: #4285f4;margin-bottom: 30px;}.search {display: flex;max-width: 600px;width: 100%;border: 1px solid #ccc;border-radius: 24px;padding: 5px 10px;}.search input {flex: 1;border: none;outline: none;font-size: 16px;}.search button {border: none;background: none;font-size: 16px;color: #4285f4;cursor: pointer;}</style>
</head>
<body><div class="logo">Boost</div><div class="search"><input type="text" id="searchInput" placeholder="请输入搜索关键字"><button onclick="jump()">🔍</button></div><script>function jump() {const input = document.getElementById("searchInput").value.trim();if (input !== "") {location.href = `search.html?word=${encodeURIComponent(input)}`;}}</script>
</body>
</html>
<!-- search.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>搜索结果 - Boost</title><script src="https://code.jquery.com/jquery-2.1.1.min.js"></script><style>body {font-family: Arial, sans-serif;background-color: #f8f9fa;margin: 0;padding: 0;}.container {max-width: 720px;margin: 0 auto;padding: 20px;}.search-bar {display: flex;margin: 20px 0;background: white;border: 1px solid #ddd;border-radius: 24px;padding: 6px 12px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);}.search-bar input {flex: 1;border: none;outline: none;font-size: 16px;padding: 8px;}.search-bar button {background-color: #4285f4;color: white;border: none;border-radius: 20px;padding: 8px 16px;font-size: 14px;cursor: pointer;}.result {margin-top: 20px;padding: 0 30px;}.result .item {margin-bottom: 25px;padding-bottom: 10px;border-bottom: 1px solid #eee;}.result .item a {display: block;font-size: 18px;font-weight: bold;color: #1a0dab;text-decoration: none;margin-bottom: 5px;}.result .item a:hover {text-decoration: underline;}.result .item p {font-size: 14px;line-height: 1.6;color: #4d5156;margin: 0;white-space: normal;/* 允许换行 */}</style>
</head><body><div class="container"><div class="search-bar"><input type="text" id="searchInput"><button onclick="jump()">搜索</button></div><div class="result"></div></div><script>const urlParams = new URLSearchParams(window.location.search);const query = urlParams.get('word') || '';document.getElementById("searchInput").value = query;if (query !== '') {Search(query);}function Search(q) {$.ajax({type: "GET",url: "/s?word=" + encodeURIComponent(q),success: function (data) {BuildHtml(data);}});}function BuildHtml(data) {const result_label = $(".result");result_label.empty();for (let elem of data) {let a_label = $("<a>", {text: elem.title,href: elem.url,target: "_blank"});let p_label = $("<p>", {text: elem.desc});let div_label = $("<div>", {class: "item"});a_label.appendTo(div_label);p_label.appendTo(div_label);  // 不再添加网址div_label.appendTo(result_label);}}function jump() {const input = document.getElementById("searchInput").value.trim();if (input !== "") {location.href = `search.html?word=${encodeURIComponent(input)}`;}}</script>
</body></html>

相关文章:

【项目实践】boost 搜索引擎

1. 项目展示 boost搜索引擎具体讲解视频 2. 项目背景 对于boost库&#xff0c;官方是没有提供搜索功能的&#xff0c;我们这个项目就是来为它添加一个站内搜索的功能。 3. 项目环境与技术栈 • 项目环境&#xff1a; ubuntu22.04、vscode • 技术栈&#xff1a; C/C、C11、S…...

接口隔离原则(ISP)

非常好&#xff0c;**接口隔离原则&#xff08;ISP: Interface Segregation Principle&#xff09;是 SOLID 五大原则中的第四个&#xff0c;它专门解决“一个接口太臃肿”**导致的麻烦。 我来从以下几个维度详细拆解&#xff1a; &#x1f9e0; 什么是接口隔离原则&#xff1…...

Leetcode刷题记录29——矩阵置零

题源&#xff1a;https://leetcode.cn/problems/set-matrix-zeroes/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述&#xff1a; 思路一&#xff1a; &#x1f4a1; 解题思路 本题中我们采用如下策略&#xff1a; 第一次遍历整个矩阵&#xff0c;记…...

复刻低成本机械臂 SO-ARM100 组装篇(打螺丝喽)

视频讲解&#xff1a; 复刻低成本机械臂 SO-ARM100 组装篇&#xff08;打螺丝喽&#xff09; 组装的视频有很多&#xff0c;参考大佬的《手把手复刻HuggingFace开源神作之Follower机械臂组装&#xff0c;资料已整理》_哔哩哔哩_bilibili&#xff0c;跟着视频做&#xff0c;大体…...

[更新完毕]2025东三省B题深圳杯B题数学建模挑战赛数模思路代码文章教学:LED显示屏颜色转换设计与校正

完整内容请看文章最下面的推广群 已经更新完整的文章代码 基于非线性映射与深度模型的多通道LED显示屏色彩校正 摘要 本研究聚焦于高动态色彩空间下LED显示屏的色彩映射与逐点校正问题&#xff0c;结合非线性回归理论与深度学习模型&#xff0c;构建了一套涵盖BT.2020映射、RG…...

Easy云盘总结篇-登录注册

**说在前面&#xff1a;该项目是跟着B站一位大佬写的&#xff0c;不分享源码&#xff0c;支持项目付费 ** 获取图形验证码 可以看到这里有2两种图形验证码&#xff0c;分为&#xff1a; type0&#xff1a;如上图下面那个&#xff0c;是完成操作后要进行注册的验证码 type1: 如…...

04 基于 STM32 的时钟展示程序

前言 我们经常会看到 各个场合下面有 基于数码管 的时钟程序 比如 在车站, 教室, 办公室 等等 各个场合都有 然后 这里就是做一个 简单的 时钟程序 展示程序 测试用例 每一秒钟更新时间, 然后 迭代更新 天, 时, 分 等等 然后 主流程 基于 天, 时分秒 渲染数码管 #incl…...

音视频开发技术总结报告

音视频开发技术总结报告 一、音视频开发基础 1、音频基础 声音原理 声波特性&#xff1a;频率、振幅、波长人耳听觉范围&#xff1a;20Hz-20kHz声音三要素&#xff1a;音调、音量、音色 数字音频基础 采样率&#xff1a;常见44.1kHz、48kHz、96kHz量化位数&#xff1a;8bit、…...

FastAPI系列13:API的安全防护

API的安全防护 1、HTTPS 强制什么是HTTPS强制如何在FastAPI中实现HTTPS强制 2、CORS跨域资源共享什么是CORS在 FastAPI 中开启 CORS 3、SQL注入防护什么是SQL注入如何在FastAPI中实现SQL注入防护 4、CSRF防护什么是CSRF防护如何在FastAPI中实现CSRF防护 在 FastAPI系列12&…...

每天一道面试题@第五天

1.包装类型的缓存机制了解么&#xff1f; 指部分包装类在创建对象时&#xff0c;会将一定范围内的对象缓存起来&#xff0c;当再次使用相同值创建对象时&#xff0c;优先从缓存中获取&#xff0c;而不是重新创建新对象。【提高性能】【节省内存】 列举几个常见的包装类缓存机…...

Python硬核革命:从微控制器到FPGA的深度开发指南

1. 重新定义硬件开发:Python的颠覆性突破 传统硬件开发长期被C/C++和Verilog/VHDL统治,但Python正通过两条路径改变这一格局: 1.1 微控制器领域的MicroPython革命 完整Python 3.4语法支持,运行在资源受限的MCU上(最低要求:64KB ROM,16KB RAM) 直接内存访问能力,突破…...

WebRTC 服务器之Janus概述和环境搭建

1 概述 Janus 是由 Meetecho 开发的通用 WebRTC 服务器&#xff0c;它为构建 WebRTC 应用程序提供了一个模块化框架。服务器目标&#xff1a;Janus WebRTC 网关被设计为轻量级、通用的 WebRTC 服务器&#xff0c;除了实现以下方法外&#xff0c;它本身不提供任何功能&#xff1…...

mcp+llm+rag

MCPRAG简介 前言一、MCP是什么&#xff1f;二、MCP工作原理&#xff08;1. MCP Hosts&#xff08;主机&#xff09;&#xff08;2.MCP Clients&#xff08;客户端&#xff09;(3. MCP Servers&#xff08;服务端&#xff09;(4. Local Data Sources&#xff08;本地数据源&…...

Seata RM的事务提交与回滚源码解析

文章目录 前言一、RM提交事务二、RM回滚事务2.1、undo校验逻辑2.2、执行回滚逻辑 总结RM 的事务提交与回滚行为说明&#xff08;基于 Seata AT 模式&#xff09;1. 提交阶段&#xff08;Phase Two Commit&#xff09;2. 回滚阶段&#xff08;Phase Two Rollback&#xff09; 前…...

Ubuntu 24.04 完整Docker安装指南:从零配置到实战命令大全

Ubuntu 24.04 完整Docker安装指南&#xff1a;从零配置到实战命令大全 文章目录 Ubuntu 24.04 完整Docker安装指南&#xff1a;从零配置到实战命令大全1. 安装 Docker2. 配置 Docker 镜像加速器2.1 配置 Docker 镜像源2.2 重启 Docker 服务 3. Docker 常用命令3.1 Docker 常用命…...

设计模式简述(十七)备忘录模式

备忘录模式 描述组件使用 描述 备忘录模式用于将对象的状态进行保存为备忘录&#xff0c;以便在需要时可以从备忘录会对象状态&#xff1b;其核心点在于备忘录对象及其管理者是独立于原有对象之外的。 常用于需要回退、撤销功能的场景。 组件 原有对象&#xff08;包含自身…...

【ICMP协议深度解析】从网络诊断到安全实践

目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键报文类型说明协议版本对比 二、实战演示环境配置要求核心实验实现实验1&#xff1a;标准ping流程实验2&#xff1a;traceroute路径发现实验3&#xff1a;自定义ICMP…...

《应用开发突围指南:敏捷开发的实战精髓》

如何在应用开发中精准且深入地应用敏捷开发方法呢&#xff1f;让我们一同深入探索。 敏捷开发&#xff0c;绝非仅仅是一种开发流程&#xff0c;更是一种蕴含深刻智慧的理念与思维方式。它与传统开发模式有着本质的区别&#xff0c;传统开发模式如同严谨的线性旅程&#xff0c;…...

【Mytais系列】SqlSession

MyBatis 的 SqlSession 是框架的核心接口之一&#xff0c;它是应用程序与 MyBatis 交互的顶层 API&#xff0c;用于执行 SQL 命令、管理事务和访问数据库。以下是关于 SqlSession 的详细说明&#xff1a; 1. 核心功能 (1) 执行 SQL 操作 增删改查&#xff1a;通过方法如 sele…...

【掌握 DDL】:SQL 中的数据库与表管理

掌握 DDL&#xff1a;SQL 中的数据库与表管理 掌握 DDL&#xff1a;SQL 中的数据库与表管理数据库 DDL创建数据库查看数据库查看所有数据库查看数据库创建语句 进入数据库删除数据库备份数据库备份恢复 查看数据库连接深入理解数据库创建与删除数据库字符集与校验规则 表 DLL创…...

第43周:GAN总结

目录 摘要 Abstract 计算机视觉中的分类 架构变体 损失变体 时间序列中的GAN 连续型GAN 离散型GAN 总结 摘要 本周总结了GAN的变形&#xff0c;主要从图像处理和时间序列生成两部分入手&#xff0c;分别找出了其中比较经典的几种GAN变种模型&#xff0c;简单分析了…...

安卓基础(MediaProjection)

1. Display 类 ​​作用​​&#xff1a;代表显示设备&#xff08;手机屏幕、外接显示器&#xff09;​​常用方法​​&#xff1a; display.getRotation() // 获取屏幕方向&#xff08;横屏/竖屏&#xff09; display.getRefreshRate() // 获取屏幕刷新率&#xff08;如&…...

Android Compose 物联网(IoT)UI 组件库封装指南

Android Compose 物联网封装组件 在物联网(IoT)应用开发中&#xff0c;使用Jetpack Compose可以创建现代化、响应式的用户界面。以下是一些针对物联网场景的Compose封装组件思路和实现方法&#xff1a; 常用物联网组件封装 1. 设备状态指示器 Composable fun DeviceStatusI…...

实用在线工具箱OmniTools

简介 OmniTools 是一个自托管的网络应用&#xff0c;提供多种在线工具&#xff0c;旨在简化日常任务。它包含了一系列独立的、小型但实用的工具&#xff0c;涵盖了文件处理、文本操作、网络请求、系统监控等多个方面。 OmniTools 的设计理念是简单、易用、可定制&#xff0c;方…...

【AI大模型学习路线】第一阶段之大模型开发基础——第三章(大模型实操与API调用)单轮对话与多轮对话调用。

【AI大模型学习路线】第一阶段之大模型开发基础——第三章&#xff08;大模型实操与API调用&#xff09;单轮对话与多轮对话调用&#xff1f; 【AI大模型学习路线】第一阶段之大模型开发基础——第三章&#xff08;大模型实操与API调用&#xff09;单轮对话与多轮对话调用&…...

数字化转型进阶:26页华为数字化转型实践分享【附全文阅读】

本文分享了华为数字化转型的实践经验和体会。华为通过数字化变革,致力于在客户服务、供应链、产品管理等方面提高效率,并把数字世界带入每个组织,构建万物互联的智能世界。华为的数字化转型愿景是成为行业标杆,通过推进数字化战略、构建面向业务数字化转型的IT组织阵型、坚…...

Go语言的优势与应用场景 -《Go语言实战指南》

一、 Go语言的五大核心优势 1. 语法简洁&#xff0c;开发高效 Go语言借鉴了C语言的表达方式&#xff0c;但去掉了多余复杂的特性&#xff08;如继承、多态、异常处理等&#xff09;&#xff0c;语法风格清晰明了&#xff0c;极大地降低了学习成本&#xff1a; • 无需头文件…...

3D人物关系图开发实战:Three.js实现自动旋转可视化图谱(附完整代码)

3D人物关系图开发实战&#xff1a;Three.js实现自动旋转可视化图谱 效果核心解析场景初始化自动旋转控制器节点创建&#xff08;带图片和标签&#xff09;关系连线动画循环数据格式说明 代码 效果 本文将带您使用Three.js实现一个带自动旋转功能的3D人物关系图谱&#xff0c;核…...

文件操作-

1. 为什么使⽤⽂件&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持久化…...

硬件零基础入门(尚硅谷)

1 一个碳原子有一个自由电子。所以能够导电。 金刚石四个都是都弄成共价键了&#xff0c;所以没有自由电子不能自由电子。 2 新的电子进来&#xff0c;因为互斥电荷进行了定向运动&#xff0c;产生了能量。两边电子平衡就停止了。所以电池的负极有电子。 电荷就是质子和电…...

【Ai零件】高德开放平台MCP的API-key注册

前言 基本操作文档&#xff0c;为n8n等平台&#xff0c;调用高德MCP服务做准备&#xff0c;本文记录其API-Key的生成步骤。 操作步骤 高德开发平台官网&#xff1a;https://lbs.amap.com/ 完成后&#xff0c;进入控制台界面: 创建新应用 进入【应用管理】&#xff0c;点击页…...

安卓基础(startActivityForResult和onActivityResult)

onActivityResult 方法有三个参数&#xff1a; requestCode&#xff1a;启动 Activity 时传入的请求码&#xff0c;用于区分不同的启动请求。resultCode&#xff1a;返回结果的状态码&#xff0c;通常为 RESULT_OK 或 RESULT_CANCELED。data&#xff1a;一个 Intent 对象&…...

安卓基础(悬浮窗)

悬浮窗 import android.app.Service; import android.content.Context; import android.graphics.PixelFormat; import android.os.IBinder; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager…...

《windows GCC 版本升级到9以上》

《windows GCC 版本升级到9以上》 在 Windows 系统上升级 GCC 到 9 以上版本通常有两种主流方案:MinGW-w64 和 WSL(Windows Subsystem for Linux)。以下是具体操作步骤: 方案一:使用 MinGW-w64(原生 Windows 环境) 步骤 1:安装 MSYS2 MSYS2 是 Windows 上的软件分发…...

LeetCode —— 102. 二叉树的层序遍历

&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️Take your time ! &#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️…...

Python面向对象编程实战:从类定义到高级特性的进阶之旅(2/10)

摘要&#xff1a;本文介绍面向对象编程基础概念&#xff0c;包括类与对象、封装、继承和多态等。以Python语言为例&#xff0c;详细讲述了类的定义与使用、构造函数与析构函数、类的访问控制等。面向对象编程通过将数据和操作封装在一起&#xff0c;提高代码的模块化和可维护性…...

【AI论文】DeepCritic:使用大型语言模型进行有意识的批判

摘要&#xff1a;随着大型语言模型&#xff08;LLMs&#xff09;的快速发展&#xff0c;对其输出提供准确的反馈和可扩展的监督成为一个紧迫而关键的问题。 利用LLM作为评判模型来实现自动化监督是一种有前景的解决方案。 在这项工作中&#xff0c;我们专注于研究和提高LLM的数…...

硬件工程师面试常见问题(12)

第五十六问&#xff1a;PCI总线基本知识 关于PCI总线的描述,错误的是:&#xff08;A&#xff09;(4分) A.PCI总线是一个16位宽的总线。 B.PCI的地址线与数据线是复用的。 C.PCI是一种独立于处理器的总线标准,可以支持多种处理器。 D.PCI支持即插即用功能。 解释&#xff1a; …...

大数据Spark(五十八):Spark Pi介绍

文章目录 Spark Pi介绍 Spark Pi介绍 Spark Pi是Apache Spark官方提供的一个示例程序&#xff0c;该案例使用 Spark 进行分布式计算&#xff0c;通过蒙特卡罗方法估算圆周率&#xff08;π&#xff09;的值&#xff0c;其估算π原理如下&#xff1a; 上图中&#xff0c;正方形…...

深入理解 HttpExchange_Java 中构建 HTTP 服务的基础组件

1. 引言 1.1 Java 中的轻量级 HTTP 服务需求 随着微服务、工具类应用和嵌入式系统的兴起,开发者对轻量级 HTTP 服务的需求日益增长。相比引入庞大的框架(如 Spring Boot),使用 JDK 原生 API 构建 HTTP 服务成为一种快速、低依赖的替代方案。 JDK 提供了 com.sun.net.htt…...

MaC QT 槽函数和Lambda表达式

在C Qt框架中&#xff0c;槽函数&#xff08;Slot&#xff09;是一种特殊的成员函数&#xff0c;用于响应信号&#xff08;Signal&#xff09;的触发&#xff0c;从而实现对象间的通信和事件处理。 #include<QMessageBox>//包含槽函数的头文件 //定义槽函数 响应特定的信…...

JMM 与 JVM 运行时数据区有什么区别和联系?

JMM&#xff08;Java Memory Model&#xff09;和 JVM 运行时数据区&#xff08;JVM Runtime Data Areas&#xff09;是 Java 内存管理中的两个不同但密切相关的概念。 1. JVM 运行时数据区 (JVM Runtime Data Areas) 是什么&#xff1f; JVM 运行时数据区是 JVM 在程序执行过程…...

LeetCode Hot100题解

目录 一、数组 & 字符串 1. 两数之和&#xff08;简单&#xff09; 2. 删除有序数组中的重复项&#xff08;简单&#xff09; 3. 移除元素&#xff08;简单&#xff09; 4. 合并两个有序数组&#xff08;简单&#xff09; 5. 买卖股票的最佳时机&#xff08;简单&…...

基于Jenkins的DevOps工程实践之Jenkins共享库

文章目录 前言Jenkins共享库结构1、共享库演示2、知识点补充3、实践使用共享库格式化输出日志4、groovy基础语法4.1、 什么是 Groovy&#xff1f;4.2、groovy特点4.3、运行方法4.4、标识符4.5、基本数据类型4.5.1、string类型4.5.2、list类型 4.6、函数使用4.7、正则表达式 5、…...

【安装指南】Docker 安装最新版 Nginx 并进行项目的编排

目录 一、Nginx 的介绍 1.1 开源版 Nginx​ ① 访问路由​ ② 反向代理​ ③ 负载均衡​ ④ 内容缓存​ ⑤ 可编程​ 1.2 商业版 Nginx Plus​ ① 负载均衡​ ② 动态管理​ ③ 安全控制​ ④ 状态监控​ ⑤ Kubernetes Ingress Controller​ ⑥ 流媒体​ 1.3 扩…...

MFC自定义控件开发与使用指南

MFC自定义控件开发与使用指南 1. 概述 MFC(Microsoft Foundation Classes)框架提供了丰富的内置控件,但在实际开发中,我们常常需要创建自定义控件来满足特定的界面需求。本文将详细介绍如何在MFC中开发自定义控件,并以CCustomTextControl为例,展示自定义控件的实现和使…...

Learning vtkjs之PolyDataNormals

法线可视化 介绍 polydata法线可视化 效果 核心代码 主要流程 const fullScreenRenderer vtkFullScreenRenderWindow.newInstance({background: [0, 0, 0],rootContainer: vtkContainerRef.current,});const renderer fullScreenRenderer.getRenderer();const renderWind…...

DeepSeek辅助学术写作之提交和出版以及评审过程分析提示词分享祝你顺利毕业~

目录 1.提交和出版 2.评审过程 大家好这里是AIWritePaper官方账号&#xff0c;官网&#x1f449;AIWritePaper~ 宝子们可以使用小编精选的“ChatGPT研究论文提示词”集合来创建研究论文。利用DeepSeek的智能回应生成详尽有效的内容&#xff0c;这样可以加快研究论文的策划、创…...

基于机器学习的心脏病数据分析与可视化(百度智能云千帆AI+DeepSeek人工智能+机器学习)健康预测、风险评估与数据可视化 健康管理平台 数据分析与处理

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…...

Kubernetes(k8s)学习笔记(四)--入门基本操作

本文通过kubernetes部署tomcat集群&#xff0c;来学习和掌握kubernetes的一些入门基本操作 前提条件 1.各个节点处于Ready状态&#xff1b; 2.配置好docker镜像库(否则会出现ImagePullBackOff等一些问题)&#xff1b; 3.网络配置正常(否则即使应用发布没问题&#xff0c;浏…...