Boost搜索引擎的实现

目录

  • Boost搜索引擎项目
    • 1.项目的相关背景
    • 2.搜索引擎的相关宏观原理
    • 3.搜索引擎技术栈和项目环境
    • 4.正排索引vs倒排索引 -搜索引擎具体原理
          • 正排索引:就是从文档ID找到文档内容(文档内的关键字)
          • 倒排索引:根据文档内容,分词,整理不重复的各个关键字,对应联系到文档ID的方案
    • 5.编写数据去标签与数据清洗的模块 Parser(分析器)
        • 理解什么是标签,以及去标签的目标
      • 编写Parser
        • boost开发库的安装
        • EnumFile的实现
        • Parser File的实现
          • 打开对应的文件
          • 提取title
          • 提取content,本质就是去标签
          • 构建url
          • Parser File实现代码
        • Save Data的实现
    • 6.编写建立索引的模块 Index
      • Index的基本代码结构
        • Build Index的实现
          • 正排索引函数的实现
          • 倒排索引的原理
          • jieba分词的使用
          • 倒排索引的实现
    • 7.编写搜索引擎模块Searcher
      • InitSearcher函数的实现
        • index改为单例模式
      • Search函数的实现
        • 摘要函数GetDesc的实现
    • 8.编写http server模块
      • 升级gcc
      • 安装cpp-httplib
      • 使用cpp-httplib
    • 9.编写前端模块
      • 编写前端代码
        • 了解html css js
        • 编写html
        • 编写css
        • 编写js
    • 10.细节优化
      • 文档重复显示问题
      • 添加一些日志信息
      • 添加去暂停词代码
    • 11.将其部署到云服务器上
      • 部署

Boost搜索引擎项目

1.项目的相关背景

  • 这里是基于boost文档所实现的站内搜索引擎,站内搜索它的数据更垂直,数据量更小
  • 一般搜索引擎搜索出来的相关数据一般含有:网页标签(title),网页内容的概要,即将要跳转的网页URL
  • Boost的官方库中是没有站内搜索功能的

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

Boost搜索引擎的实现_第1张图片

3.搜索引擎技术栈和项目环境

  • 技术栈:C/C++,STL,准标准库,Boost库,Jsoncpp(客户端和服务器端进行交互的时候所需要进行序列化和反序列化),cppjieba(给搜索关键字进行分词),cpp-httplib(一个开源的http的开源库,可以直接构建http服务器) html5,css,js,jQuery,Ajax
  • 项目环境:Centos 7云服务器vim/g++/Makefile,vscode/vs2019

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

正排索引:就是从文档ID找到文档内容(文档内的关键字)
文档ID 文档内容
1 新海诚上映了新电影
2 新海诚的新电影是铃芽户缔

对目标文档进行分词(目的:方便建立倒排索引和查找):

  • 文档1:新海诚/上映/了新电影
  • 文档2:新海诚/的新电影/是/铃芽户缔

停止词:了,的,吗,a,the,一般我们在分词的时候可以不考虑

倒排索引:根据文档内容,分词,整理不重复的各个关键字,对应联系到文档ID的方案
关键字(具有唯一性) 文档ID,weight(权重高的在前)
新海诚 文档1,2
上映 文档1
新电影 文档1,2
铃芽户缔 文档2

这样就可以模拟一次查找的过程:

用户输入:新海诚-->倒排索引中查找-->找到之后提取出文档ID(1,2)-->根据正排索引--->找到文档内容 -->title+conent(desc) +url 文档结果进行摘要--->构建响应结果

5.编写数据去标签与数据清洗的模块 Parser(分析器)

boost官网:https://www.boost.org/
//目前只需要boost_1_81_0/doc/html目录下的html文件,用它来建立索引

在这里插入图片描述
之所以选doc/html文件也是因为官网之中绝大部分使用的都是这里面的文件

理解什么是标签,以及去标签的目标
[xifeng@VM-16-14-centos MyBoostSearch]$ touch parser.cc
//想要做数据处理就需要有原始数据-->去标签之后的数据
//标签也就是<>之中包含的,标签对搜索没有价值,需要被去除
//一般标签都是成对出现的

Boost搜索引擎的实现_第2张图片

//html放的是原始文档
[xifeng@VM-16-14-centos data]$ html
[xifeng@VM-16-14-centos data]$ purify_html
//去除掉标签之后的html我们保存在puritf_html中(puritf提纯/去除)
[xifeng@VM-16-14-centos html]$ ls -Rl | grep -E '*.html' | wc -l
8429 //一共有8429个html文件
目标: 把每个文档都去标签,然后写入到同一个文件中,文档内容不需要换行(\n),文档和文档之间用\3区分
类似于:xxxxxxx\3xxxxxxxxx\3xxxxxxxxxxxx\3 但是这种方法虽然可行,可是后续处理的时候会比较麻烦,因为还需要去区分title,content,url的内容
---------------------------------------------------
所以我选择的是这种操作:title\3content\3url \n title\3content\3url \n....
就是同一个文档之间不同的数据之间采用\3来区分,文档文档之间采用\n来区分,这样既可以通过getline获取到一个文档的全部信息,又方便区分文档中的不同信息

Boost搜索引擎的实现_第3张图片

之所以选择\3也是有一定的理由的:首先是因为它是控制字符,不会显示到文件中,其次就是\3也有正文结束的意思

编写Parser

  • 首先在purify_html中创建了一个文本文件purify.txt用来保存清洗之后的数据

主要有三大块:

  1. 首先要将所有的文件给拿到,可以将带路径的文件名存放到一个数组中(Enum File通过这个函数来枚举各个文件的路径)

  2. 其次就是对文件进行读取和解析(Parser File通过这个函数来解析)

    • 解析数据要解析成什么样子的呢

    • 可以定义一个结构体

      typedef struct Docinfo
      {
      	std::string title;//标签
          std::string content;//内容
          std::string url;//官网所对应的url
      }Docinfo_t;
      
  3. 最后就是将解析到的数据存放在purity.txt中(Save Data通过这个函数来存储数据)

//代码的大致框架
#include
#include
#include
#include
const std::string src_path = "./data/html";
const std::string save_path= "./data/purify_html/purify.txt";
typedef struct Docinfo
{
  std::string title;//文档的标签
  std::string content;//文件的内容
  std::string url; //文件对应官网中的url
}Docinfo_t;
//注意:输入型参数我使用:const &
//输出型参数:*
//输入输出型参数:&
bool EnumFile(const std::string& src_path,std::vector* file_list);
bool ParserFile(const std::vector& file_list,std::vector* data_list);
bool SaveData(const std::string& save_path,const std::vector& data_list);
int main()
{
  //第一步:获取到文件中的所有的html路径,并将其存放到一个数组当中
  std::vector file_list;//文件列表
  if(!EnumFile(src_path,&file_list))
  {
    //如果遍历失败,后续就没有意义了,直接退出
    std::cerr<<"EnumFile error"< data_list;//数据列表
  if(!ParserFile(file_list,&data_list))
  {
    //同样解析失败也就是退出
    std::cerr<<"ParserFile error"<
boost开发库的安装

sudo yum install -y boost-devel,我安装的是1.53版本的boost库,这意味的是用1.53的库去写代码,我搜索的文档还是1.81的文档,这两个并不冲突

EnumFile的实现
  • 首先需要通过boost库了解一些相关的接口,这里对文件的操作使用的是boost库中提供的filesystem(头文件:)

  • 在官方文档的使用样例中,对于命名空间这一块他使用的是namespace fs = boost::filesystem;,所以在我的代码中也就不全局放开了,与文档保持一致,在使用的时候通过域作用限定符去写,不去无脑放开还是很有好处的,能很大程度的减少接口上的冲突

    //一下是需要了解的部分类或者接口
    //path类里面定义了一个root_path
    path root_path() const;
    Returns: root_name() / root_directory()
    -----------------------------------------
    path operator/ (const path& lhs, const path& rhs);
    Returns: path(lhs) /= rhs.
    // /=是被重载了的,作用:将优选目录分隔符附加到包含的路径名,除非:
    //一个添加的分离器将是多余的,或者会将相对路径更改为绝对路径,或P.Empty()或*p.native()。cbegin()是目录分离器。
    //root_name() 如果包含根名则返回根名
    //root_directory() 如果包含根目录返回根目录 
    ------------------------------------------
    //exists用来判断文件是否存在
    bool exists(const path& p);
    //递归遍历
    Class recursive_directory_iterator
    //类型的对象提供标准库 对目录内容进行合规迭代,包括递归到其子目录
    //进行迭代的时候需要考虑要遍历的文件必须是普通文件,必须是.html的,所以这就需要对文件名进行筛选
    //判断是否是常规文件
    bool is_regular_file(const path& p);
    //用来判断后缀的是path类内部的一个成员函数
    path extension(const path& p) const;
    //可以看一下文档举的例子
    std::cout << path("/foo/bar.txt").extension(); // outputs ".txt"
    //这里可以发现调用返回的是".txt",这样就可以进行判断
    //当判断完之后我们就需要将其push到存放地址的数组中去,path类中提供了一个string方法
    template 
    String string(const codecvt_type& cvt=codecvt()) const;
    //返回的是string类型的路径
    

EnumFile具体的实现:

#Makefile编写时,如果不加 -lboost_system和-lboost_filesystem会报错的
#因为boost不是标准库,第三方库链接时需要指明库名称
cc=g++
parser:parser.cc
  $(cc) -o $@ $^ -std=c++11 -lboost_system -lboost_filesystem            .PHONY:clean                                                  
clean:
  rm -rf parser
//作用类似于typedef,跟文档样例保持一致,写代码时最好不要遇到命名空间就全部展开
namespace fs = boost::filesystem;
bool EnumFile(const std::string& src_path,std::vector* file_list)
{
  //boost库中定义的成员,简单理解就是将我们传入的字符串转换成boost能够识别的路径
  fs::path root_path(src_path);
  if(!exists(root_path))
  {
    //代表目标文件不存在
    return false;
  }
  //到这里就是已经找到了目标文件,可以进行迭代查找了
  fs::recursive_directory_iterator end;//定义一个结束的迭代器
  //构建一个从root_path开始的迭代器
  for(fs::recursive_directory_iterator it(root_path);it!= end;++it)
  {
    //要查找首先得是一个普通文件
    if(!fs::is_regular_file(*it))
    {
      continue;
    }
    //其次需要的是.html后缀的文件
    if(!(it->path().extension()==".html"))
    {
      continue;
    }
    //到这里就是后缀名为.html的普通文件了,将其存放进vector即可
    //如果直接push_back(*it)是不正确的,因为那个已经不是string类型了
    //我们可以通过boost提供的string接口来将其转化一下
    file_list->push_back((it->path()).string());
  }
  return true;
}
Parser File的实现

总体思路:首先已经拿到了所有的带路径的文件名,接下来要做的就是对这些文件进行遍历,打开每一个文件进行读写,将其解析成Docinfo_t这样的结构将其插入到vector中

打开对应的文件
  • 首先需要了解几个相关函数,在c++中对于文件操作的头文件是#include
  • ifstream是输入流,其构造函数explicit ifstream (const char* filename, ios_base::openmode mode = ios_base::in); 注意一下ios和ios_base没有区别,都可以用
  • open和close分别对应着打开文件和关闭文件;void open (const char* filename, ios_base::openmode mode = ios_base::in); void close();
  • is_open是判断文件是否被打开bool is_open();
  • getline是按行读取,需要将读到的字符拼接成一个长字符串istream& getline (istream& is, string& str);
//以下是具体代码
//这里对c++提供的接口进行了简单的封装
bool Open(const std::string &file_path, std::string *out)
{
     std::ifstream in(file_path, std::ios_base::in);
     if (!in.is_open()) // 如果文件没有被打开则返回false
     {
        // 文件如果打开失败打印失败的文件名
        std::cerr << "Open file error:" << file_path << std::endl;
        return false;
     }
    //文件能够被成功打开
    std::string str;
    //理解getline读取到文件结尾:getline的返回值是一个&,while判断的是一个bool类型,本质就是返回的类型中重载了强制类型转换
    while(std::getline(in,str))//按行读,最后拼接在一起
   	{
       *out +=str;
    }
    in.close();
   	return true;
}
提取title

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ADIrHGnD-1679760493442)(C:/Users/yangyr0206/AppData/Roaming/Typora/typora-user-images/image-20230314143742011.png)]

  • 通过STL容器string提供的find函数可以去查找字符串""和"</titile>"</code></p> <pre><code class="prism language-c++">size_t find (const string& str, size_t pos = 0) const; //如果找不到就会返回std::npos; //找到就返回找到的第一个字符对应的位置下标,比如<title>找到就返回<的下标 </code></pre> </li> <li> <p>通过substr去获取中间的片段</p> <pre><code class="prism language-c++">string substr (size_t pos = 0, size_t len = npos) const; //pos是从什么位置开始,len是取多长 </code></pre> </li> </ul> <pre><code class="prism language-c++">//实现 bool ParserTitle(const std::string &result, std::string *title) { // 要提取的就是<title>之间的数据 size_t begin = result.find(""); size_t end = result.find(""); if (begin == std::string::npos || end == std::string::npos || begin > end) { std::cerr << "find error" << std::endl; return false; } // 两个都找到了,且begin < end begin += 7;//为了跳过 *title = result.substr(begin, end - begin); // 左闭右开区间 return true; } </code></pre> <h6>提取content,本质就是去标签</h6> <p>在进行遍历时,无论是单标签还是双标签,只要碰到了<code>></code>,就意味着,当前的标签被处理完毕;只要读到了<code><</code>意味着新的标签开始了</p> <pre><code class="prism language-c++">//实现 bool ParserContent(const std::string &result, std::string *content) { enum Status // 状态机的状态码 { START, // 开始 OVER // 结束 }; // 去标签,当遇到>我们认为一个标签结束了,遇到<表示一个标签刚刚开始 // 由此可以设置一个简单的状态机 enum Status st = START; // 默认是标签的开始 for (char c : result) { switch (st) { case START: if (c == '>') st = OVER; break; case OVER: if (c == '<') st = START; else // 正文内容 { if (c == '\n') c = ' '; content->push_back(c); } break; default: break; } } return true; } </code></pre> <h6>构建url</h6> <p>boost库的官方文档,和我们下载下来的文档,是有路径的对应关系的</p> <pre><code class="prism language-url">官网的URL样例:https://www.boost.org/doc/libs/1_81_0/doc/html/accumulators.html 下载下来的URL样例:boost_1_81_0/doc/html/accumulators.html 我项目中的URL样例:MyBoostSearch/data/html/accumulators.html //本质就是我将下载下来doc/html/* cp data/html/ ---------------------------------------------------------------- url_head = "https://www.boost.org/doc/libs/1_81_0/doc/html"; url_tail = [data/html](delete) /accumulators.html--->"/accumulators.html"; url = url_head + url_tail;//相当于形成了一个官网链接 </code></pre> <pre><code class="prism language-c++">const std::string head_url = "https://www.boost.org/doc/libs/1_81_0/doc/html"; bool ParserUrl(const std::string &head_url, const std::string &file, std::string *url) { // 就是进行平接只需要把我的路径中的./data/html-->也就是之前定义的src_path去除与head_url拼接即可 std::string tail_url = file.substr(src_path.size()); *url = head_url + tail_url; return true; } </code></pre> <h6>Parser File实现代码</h6> <pre><code class="prism language-c++">bool ParserFile(const std::vector<std::string> &file_list, std::vector<Docinfo_t> *data_list) { for (const std::string &file : file_list) { // 1.打开文件 // result(结果)用来存放文件读出来的数据 std::string result; if (!Tool::Open(file, &result)) { continue; } // 2.解析成Docinfo_t类型的数据 // 提取标签,提取内容,拼接url Docinfo_t doc; // 获取标签 if (!ParserTitle(result, &doc.title)) { continue; } // 获取内容 if (!ParserContent(result, &doc.content)) { continue; } const std::string head_url = "https://www.boost.org/doc/libs/1_81_0/doc/html"; // 拼接url if (!ParserUrl(head_url, file, &doc.url)) { continue; } //move函数主要作用就是数据的移动,也就是说moved-from对象处于有效但未指定的状态。这意味着,在这样的操作之后,移出对象的值只应被销毁或分配一个新值;否则,访问它会生成未指定的值。在这里就是doc里面的数据就不再是有效数据了 //如果不加move会发生拷贝,而一个网页数据有的还是挺大的,拷贝的话需要浪费很多时间 data_list->push_back(std::move(doc)); } return true; } </code></pre> <h5>Save Data的实现</h5> <p>就是将前面得到的data_list里面的数据全部按照制定的规则存放到purify.txt文件中即可</p> <pre><code class="prism language-c++">bool SaveData(const std::string &save_path, const std::vector<Docinfo_t> &data_list) { std::ofstream out(save_path, std::ios_base::out | std::ios_base::binary); //要实现的就是同一网页的title,content,url通过\3分隔,不同网页通过\n分隔 //在文件中'\3'是以^C的形式体现的 for(auto & data : data_list) { //打开文件,以二进制的形式写入 if(!out.is_open()) { std::cerr<<"open savefail error"<<std::endl; return false; } std::string str; str+=data.title; str+='\3'; str+=data.content; str+='\3'; str+=data.url; str+='\n'; out.write(str.c_str(),str.size());//读哪里,读多长 } out.close(); return true; } //之后查看save_path路径下的文件 cat purify.txt | wc -l ---->8429行也就是说里面有8429个文件 </code></pre> <h3>6.编写建立索引的模块 Index</h3> <ul> <li> <p>首先需要建立index.hpp文件,在其中定义一个数据的数据结构Docinfo</p> </li> <li> <p>需要建立倒排索引的节点(文档id,权重,关键字)</p> </li> <li> <p>正排索引的数据结构选择数组,这样的话当知道文档id的话就可以根据下表直接找到(时间复杂度就为O(1))</p> </li> <li> <p>倒排索引一定会存在一个关键字和多个文档id(一个)有关联,所以倒排索引天然的就适合使用unordered_map</p> </li> <li> <p>字符串切分虽然用stl容器提供的接口也能够去写,可是比较繁琐,可以使用boost库中的split(<boost/algorithm/string.hpp>)</p> <pre><code class="prism language-c++">boost::split(type,str,boost::is_any_of("\3"),boost::token_compress_on) //第一个参数type就是一个用来存放切分后数据的数据结构 //str就是要切分的字符串 //boost::is_any_of()里面设置的是分隔符 //boost::tocken_compress_on:将连续多个分隔符压缩成一个,默认没打开,一般用的时候打开 </code></pre> </li> </ul> <h4>Index的基本代码结构</h4> <pre><code class="prism language-c++">// 这里是构建索引模块 #pragma once #include <iostream> #include <string> #include <unordered_map> #include <vector> namespace MyIndex { // 文档信息 struct Docinfo { std::string title; std::string content; std::string url; uint64_t file_id; // 因为有正排和倒排索引,所以文档id是不可或缺的 }; // 倒排元素 struct InvertedElement { std::string key_word; uint64_t file_id; int weight; // 权重,之后显示的先后顺序需要与权重挂钩 }; //重命名为倒排拉链 typedef std::vector<InvertedElement> InvertedList; class Index { private: // 正排索引的数据结构 // 因为正排索引需要的是根据文档id去找文档内容,文档id可以当做数组下标能够实现O(1)的查找 std::vector<Docinfo> forward_index; // 倒排索引的数据结构 // 根据关键字去找文件id,这天然就是一对多(一对一)的关系 // 所以用unordered_map最为合适 std::unordered_map<std::string, InvertedList> inverted_index; public: Index(); ~Index(); public: // 获取正排-->返回的是文档信息 Docinfo *GetForward(const uint64_t &id) { //这类比较简单就直接写了 if(id > forward_index.size()) { //id越界 std::cerr<<"id cross the border error"<<std::endl; return nullptr; } return &forward_index[id]; } // 获取倒排 -->返回的是倒排拉链,也就是一个关键字对应的一组文件id InvertedList* GetInverted(const std::string &key_word) { auto it = inverted_index.find(key_word); if(it==inverted_index.end()) { std::cerr<<"not found"<<std::endl; return nullptr; } return &(it->second); } // 构建索引 --->根据的就是解析后的./data/purify_html/purify.txt里面的内容 //const std::string target_path = "./data/purify_html/purify.txt"; // 目标文件的路径 bool BuildIndex(const std::string &input)//在这里面去构建对应的索引模块 { return true; } }; }; </code></pre> <p>接下来就是对上述函数的实现</p> <h5>Build Index的实现</h5> <ul> <li> <p>要构建索引就需要数据,而这数据就是Parser解析出来的存放在<code>./data/purify_html/purify.txt里面的内容</code></p> </li> <li> <p>所以理所应当的在Build Index中需要进行文件操作</p> <pre><code class="prism language-c++"> bool BuildIndex(const std::string &input) { // 之前是二进制形式写,这里也是二进制形式读 std::ifstream in(input.c_str(), std::ios_base::in | std::ios_base::binary); if (!in.is_open()) { std::cerr << "open error" << std::endl; return false; } std::string line; while (std::getline(in, line)) { // 构建正排-->返回的是Docinfo然后需要通过返回值去构建倒排索引 Docinfo *doc = BuildForward(line); if (doc == nullptr) { std::cerr << "BuildForward error" << std::endl; continue; // 一个文档的正排构建失败就没有必要再去构建倒排了 } // 构建倒排 BuildInverted(*doc); } in.close(); return true; } </code></pre> </li> </ul> <h6>正排索引函数的实现</h6> <ul> <li> <p>需要使用到之前提到的boost库中提供的split函数</p> <pre><code class="prism language-c++">void SlicingString(const std::string& line,std::vector<std::string>*result, const std::string& separator) { //可以通过stl提供的容器接口来实现,可是实现起来比较繁琐 //所以这里直接使用boost库中的<boost/algorithm/string.hpp> split boost::split(*result,line,boost::is_any_of(separator),boost::token_compress_on); //*result 存放切分的数据的结构vector<string> //line 要切分的数据 //is_any_of() 构建切分符 //token_compress_on将连续的分隔符压缩成一个分隔符(默认关闭,需要手动打开) } </code></pre> </li> </ul> <pre><code class="prism language-c++">Docinfo *BuildForward(const std::string &line) { std::vector<std::string> result; const std::string separator = "\3"; MyTool::StringTool::SlicingString(line, &result, separator); if (result.size() != 3) { // 如果切分后的数据没有三部分则切分出错 return nullptr; // 切分失败返回空 } // 将切分的数据填充到doc中 Docinfo doc; doc.title = result[0]; doc.content = result[1]; doc.url = result[2]; doc.file_id = forward_index.size(); // 先保存在push,这样可以让id和数组下标对应,比如一开始什么都没有size=0,文档id=0,当这个doc push进去之后它所在的下标也是0 // 将doc 插入到正排索引的vector中 forward_index.push_back(std::move(doc));// 通过拷贝效率太低,直接move可以提升效率 return &forward_index.back(); } </code></pre> <h6>倒排索引的原理</h6> <pre><code class="prism language-c++">//原理:就是需要构建出多个这样的结构,之前已经拿到了Docinfo的数据 struct InvertedElement { std::string key_word; uint64_t doc_id; int weight; // 权重,之后显示的先后顺序需要与权重挂钩 }; //倒排拉链 typedef std::vector<InvertedElement> InvertedList; //倒排索引一定是一个key_word和一组(一个)InvertedElement对应 //通过正排索引可以拿到的内容 struct Docinfo { std::string title; std::string content; std::string url; uint64_t file_id; }; //举例文档: title:新电影铃芽户缔 content:新海诚出了一部新的电影名叫铃芽户缔 url:http://XXX doc_id: 324 根据文档内容形成一个或多个InvertendElement(倒排拉链) 因为是一个一个的对文档进行处理的,一个文档会包含多个词,都对应到当前的doc_id 1. 需要对title和content进行分词 --这里使用jieba分词-->CutForSearch(s,words) titile:新/电影/新电影/铃芽/户缔/铃芽户缔 title_words content:新/海诚/新海诚/出/一部/新/电影/电影名/叫/铃芽/户缔/铃芽户缔 content_words 词和文档的相关性(相关性的实现并不是一个简单的技术,这里用最简单的方式去实现) 2. 词频统计 这里我实现相关性的方案就是根据词频来设置,同时我认为在标题中出现的词相关性会更高一些,在内容中出现相关性低一些 struct word_count{ int title_count;//标题中key_word出现的次数 int content_count;///内容中key_word出现的次数 }; //建立关键字和出现次数的映射关系 unordered_map<std::string,word_count> words_count; for &word : title_word{ word_count[word].title_count++; } for &word : content_word{ word_count[word].content_count++; } 知道了文档内容中词的出现次数 3. 自定义相关性 for &word : words_count{ InvertendElement elem; //因为处理的是同一个文档的内容,所以文档的id是可知的 elem.doc_id = 324; elem.key_word = word.first; elem.weight = 10*word.second.title.count + 1* word.second.content_count; inverted_index[word.first].push_back(elem); } //s要切部分 //words切后存储的结构 //string s; //boost::to_lower(s)将字符串转化成小写 //c也提供了一些函数接口:toupper()//转换为大写,tolower()//转换为小写 //c++提供的接口可以用:transform(s.begin(),s.end(),s.begin(),::toupper); //几个参数简单理解就是从哪开始,到哪结束,结果储存到哪,转成大写还是小写 </code></pre> <h6>jieba分词的使用</h6> <ul> <li> <p>获取链接:git clone https://gitcode.net/mirrors/yangyiwu/cppjieba.git</p> </li> <li> <p>如何使用:需要的是include/cppjieba/Jieba.hpp</p> </li> <li> <p>文件中有个test/demo.cpp的文件里面是jieba分词的用法</p> </li> <li> <p>如果想要正确编过,需要将deps/limonp里面的所有文件都拷贝进include/cppjieba中,我这里使用切词的函数是里面的:CutForSearch(s,words) ---->s表示要切分的数据,words表示存放的数据</p> <pre><code class="prism language-c++">//在下载后的cppjieba文件中,有一个test文件里面有个demo.cpp文件,里面记录了各种接口函数的用法以及样例 //接下来来看看CutForSearch(s,words)函数在文档中的用法 #include "../include/cppjieba/Jieba.hpp" #include<iostream> #include<string> #include<vector> using namespace std; //这些const定义的是词库的所在路径,自己使用的时候需要注意调整这些路径 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"; int main(int argc, char** argv) { cppjieba::Jieba jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_PATH, STOP_WORD_PATH); string s; vector<string> words; s = "小明硕士毕业于中国科学院计算所,后在日本京都大学深造"; cout << s << endl; cout << "[demo] CutForSearch" << endl; jieba.CutForSearch(s, words); cout << limonp::Join(words.begin(), words.end(), "/") << endl; return 0; } //我这里采用ln -s软链接的方式去调整路径 //结果: //小明硕士毕业于中国科学院计算所,后在日本京都大学深造 //[demo] CutForSearch ///小明/硕士/毕业/于/中国/科学/学院/科学院/中国科学院/计算/计算所/,/后/在/日本/京都/大学/日本京都大学/深造 </code></pre> <pre><code class="prism language-c++">#include "./cppjieba/Jieba.hpp" //我在当前目录设置了一个cppjieba的软连接 //lrwxrwxrwx cppjieba -> data/cppjieba/include/cppjieba 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"; class Cppjieba { private: static cppjieba::Jieba jieba;//要定一个全局的静态成员变量,这样不需要每一次调用CutForSearch都去先创建一个jieba对象,大大的节省了时间 //因为jieba分词在index建立索引中会非常频繁的使用,所以一旦在函数里面定义jieba对象就会让整个程序变的很慢 public: static void CutForSearch(const std::string &s, std::vector<std::string> *words) { jieba.CutForSearch(s, *words); } }; cppjieba::Jieba Cppjieba::jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_PATH, STOP_WORD_PATH); </code></pre> </li> </ul> <h6>倒排索引的实现</h6> <pre><code class="prism language-c++">bool BuildInverted(const Docinfo &doc) { #define T_weight 10 #define C_weight 1 struct word_count{ int title_cnt;//标题key_word出现次数 int content_cnt;//内容key_word出现次数 }; //1.需要对标题和内容进行切分 && 2.进行词频统计 std::vector<std::string> title_result;//存放切分后的数据 std::unordered_map<std::string,word_count> word_cnt;//建立key_word和词频的映射 MyTool::Cppjieba::CutForSearch(doc.title,&title_result); for(auto word : title_result)//遍历标题分词 { //这里有个细节,Hello hello HELLO这些词是算一个,还是算三个-->通过百度浏览器的搜索结果可以发现搜索时是不区分大小写的 //这里就定个规定:文档的标题和正文分词全部按照小写来分词,同时用户输入之后也将其转换成小写 //可以用c提供的toupper()--->转换为大写,tolower()---->转换为小写 /c++ <algorithm>中的transform等等 //我选择的就是boost库提供的一个接口to_lower() boost::to_lower(word);//因为我不想改变doc里面的数据,所以没有用引用 word_cnt[word].title_cnt++;//unordered_map重载了[],如果key存在时返回的就是value的引用,如果不存在就插入key } std::vector<std::string> content_result; MyTool::Cppjieba::CutForSearch(doc.content,&content_result); for(auto word : content_result) { boost::to_lower(word); word_cnt[word].content_cnt++; } //3.构建相关性 for(auto& word : word_cnt) { //word 在这里是unordered_map<string,vector<word_count>> InvertedElement elem; //这里处理的都是一个文档的分词,所以id就是doc里面的file_id elem.file_id = doc.file_id; elem.key_word = word.first; elem.weight = T_weight* word.second.title_cnt + C_weight* word.second.content_cnt;//设置相关性 //unordered_map<string,vector<InvertedList>> inverted_index; InvertedList& inverted_list = inverted_index[word.first]; inverted_list.push_back(std::move(elem));//这里加不加move都还好,里面数据比较小 } return true; } </code></pre> <h3>7.编写搜索引擎模块Searcher</h3> <p>基本代码结构:</p> <pre><code class="prism language-c++">//安装jsoncpp:yum install -y jsoncpp-devel void InitSearcher(const std::string &path) { // 1.构建/获取index对象--->因为对于index,建立完之后主要就是查找,并不会修改里面的内容,所以index只需要一份即可 // 也就是把index设计成一个单例即可 } //这个就是通过用户的搜索信息去索引中去查找相关文档,并输出序列化之后的字符串 void Search(const std::string &Inquire, std::string *json_string) { // 1.分词:将搜索Inquire进行分词 // 2.查询:分词之后用关键词去索引表中查找,如果有会拿到倒排拉链InvertedList-->vector<InvertedElement> // 3.合并排序:将查找后的结果通过weight权重进行降序排序 // 4.构建:根据查询结果构建json字符串--->也就是序列化,需要jsoncpp } </code></pre> <h4>InitSearcher函数的实现</h4> <h5>index改为单例模式</h5> <ul> <li> <p>原因:我所实现的搜索引擎只是对boost进行搜索,所以理论上并不需要多份索引,构建索引也需要消耗资源,只需要构建一份之后其他的共用即可,这里就需要将index改为单例,具体改法如下:</p> <pre><code class="prism language-c++">//在index类中设置一个staitc的index对象,和一把锁(解决线程安全问题,如果是多线程不加锁,对单例的获取存在线程安全问题) static Index *singleton_index; static std::mutex mtx; // 创建一把锁---->#include<mutex> //之后就是说构造函数私有化-->必须要有,因为这样才能够在类内new一个index对象 //禁止构造和拷贝构造函数 Index(const Index &in) = delete; // 禁止拷贝构造 Index &operator=(const Index &in) = delete; // 禁止赋值拷贝 //之后就是在public中定义一个静态的成员函数 static Index *GetIndex()// 静态成员函数才能访问静态成员变量 { // 两个if是为了提高效率,多个线程竞争锁也是需要消耗资源的, if (singleton_index == nullptr) { // 不加锁的话多线程情况下不是线程安全的 mtx.lock(); // 加锁 if (singleton_index == nullptr) { singleton_index = new Index; } mtx.unlock(); } return singleton_index; } </code></pre> </li> </ul> <pre><code class="prism language-c++">//构建好单例之后直接使用接口即可 index = MyIndex::Index::GetIndex(); index->BuildIndex(path); </code></pre> <h4>Search函数的实现</h4> <pre><code class="prism language-c++">struct Compare//定义一个降序的仿函数用于sort的第三个参数 { bool operator()(const MyIndex::InvertedElement &e1, const MyIndex::InvertedElement &e2) { return e1.weight > e2.weight; } }; void Search(const std::string &Inquire, std::string *json_string) { // 1.分词:将搜索Inquire进行分词 std::vector<std::string> words; MyTool::Cppjieba::CutForSearch(Inquire, &words); // 2.查询:分词之后用关键词去索引表中查找,如果有会拿到倒排拉链InvertedList-->vector<InvertedElement> MyIndex::InvertedList result;//different for (std::string key_word : words) { boost::to_lower(key_word); // 建立索引的时候默认全是小写,搜索的时候也同样需要 // 先查倒排,再根据查询后的结果查正排 MyIndex::InvertedList *inverted_list = index->GetInverted(key_word); if (inverted_list == nullptr) // 如果词不存在,就继续下一次查找 { continue; } // 这个就是通过迭代器将InvertedList里面的InvertedElement全部插入到result中 result.insert(result.end(), inverted_list->begin(), inverted_list->end()); } // 3.合并排序:将查找后的结果通过weight权重进行降序排序 std::sort(result.begin(), result.end(), Compare()); // 4.构建:根据查询结果构建json字符串--->也就是序列化,需要jsoncpp Json::Value root; //Value是json里面的万能类 //json序列化通常使用Wright类(FastWriter,StyledWriter) 反序列化通常是Reader类 for(auto& elem : result) { //根据result里面的数据,去正排查找文档并且把查找到的文档序列化 MyIndex::Docinfo* doc = index->GetForward(elem.file_id); Json::Value tmp; tmp["title"] = doc->title;//标题 tmp["desc"] = GetDesc(doc->content, elem.key_word);//描述 tmp["url"] = doc->url;//要跳转的网页 root.append(tmp); } //要注意无论是FastWriter还是StylenWriter它们都是类,而write是它们的成员函数,所以需要先构建一个匿名对象来使用它的成员函数 //*json_string = Json::FastWriter().write(root);//StyleWriter方便调试,所以先用,后面没有问题之后再用这个 *json_string = Json::StyledWriter().write(root);//这样用户就获得了通过权重排序之后的文档信息 } </code></pre> <p><strong>注意:</strong><code>jsoncpp</code>是第三方库所以使用g++编译的时候需要指定库<code>-ljsoncpp</code></p> <h5>摘要函数GetDesc的实现</h5> <pre><code class="prism language-c++"> struct GetCompare//定义了一个仿函数 { bool operator()(int x, int y) { return std::tolower(x) == std::tolower(y); } }; std::string GetDesc(const std::string &content, const std::string &key_word) { // 获取描述---->这里就简易实现一下 // 获取第一次出现的key_word的前50个字符,获取key_word的后100个字符,如果不够就从头开始,或者截取到尾部 const int prev = 50; // 因为size_t是无符号整数,所以为了方便就直接设置为int const int next = 100; // int pos = content.find(key_word); // 这里查找会出现None1的情况,主要是因为在之前的代码中我对切分后的数据统一to_lower了,但是find函数在查找的时候不会自己进行大小写转化 // 这里使用C++的<algorithm>提供的算法search auto it = std::search(content.begin(), content.end(), key_word.begin(), key_word.end(), GetCompare()); if (it == content.end()) return "None"; // 如果到结尾都没有查到就返回None---不会发生 int pos = std::distance(content.begin(), it); int start = 0; int end = content.size() - 1; // 代表前面有50个字符 if (pos - prev > start) start = pos - prev; // 代表后面有100个字符 if (end - next > pos) end = pos + next; if (start >= end) // 同理 return "None"; std::string desc = content.substr(pos, end - start); desc += "......"; return desc; } </code></pre> <ul> <li> <p>C++的<code>#include<algorithm></code>提供的search函数</p> <pre><code class="prism language-c++">template <class ForwardIterator1, class ForwardIterator2> ForwardIterator1 search (ForwardIterator1 first1, ForwardIterator1 last1,ForwardIterator2 first2, ForwardIterator2 last2,BinaryPredicate pred); //简单点理解就是1,2两个参数是要查找数据的迭代器:意味着从哪开始查到哪结束比如:string str = "hello word"; //3,4两个参数是查找目标的迭代器string key = "word"; 在str中去查找key //第5个参数可以理解为传入查找方法,我这里是传入的一个GetCompare的仿函数 </code></pre> </li> <li> <p><code>#include <iterator></code>提供的函数<code>destance</code>可以用来查看迭代器相比于begin走了多远</p> <pre><code class="prism language-c++">template<class InputIterator> typename iterator_traits<InputIterator>::difference_type distance (InputIterator first, InputIterator last); //用法 std::list<int> mylist; for (int i=0; i<10; i++) mylist.push_back (i*10); std::list<int>::iterator first = mylist.begin(); std::list<int>::iterator last = mylist.end(); std::cout << "The distance is: " << std::distance(first,last) << '\n'; return 0; //result:The distance is 10 </code></pre> </li> </ul> <h3>8.编写http server模块</h3> <h4>升级gcc</h4> <pre><code class="prism language-c++">如果想使用cpp-httplib库 注意: centos 7 默认的gcc编译器的版本是4.8.5版本的,如果想要使用cpp-httplib是需要更新gcc编译器的,如果用老的会编译不通过,或者运行时报错 更新gcc方法,搜索关键字: scl gcc devsettool升级gcc 1.安装scl源: yum install centos-release-scl scl-utils-build 2.安装新版本gcc sudo yum install -y devtoolset-7-gcc devtoolset-7-gcc-c++ 安装好之后,工具集在ls /opt/rh中 //启动新版本的gcc:命令行启动只能在本会话有效 scl enable devtoolset-7 bash //建议将上面的启动命令添加到~/.bash_profile这个登录脚本里面 # .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/.local/bin:$HOME/bin export PATH #每次启动的时候都会执行scl这个命令 scl enable devtoolset-7 bash </code></pre> <h4>安装cpp-httplib</h4> <pre><code class="prism language-c++">最新的cpp-httplib在使用的时候,如果gcc不是特别新的话可能会有运行时错误 我这里使用的是cpp-httplib 0.7.15版本 通过gitee搜索cpp-httplib,下载zip文件上传到服务器即可 </code></pre> <h4>使用cpp-httplib</h4> <pre><code class="prism language-c++"> inline bool Request::has_param(const char *key) const { return params.find(key) != params.end();//他这里面的params是一个multimap<std::string, std::string>类型 } //Server端的使用 //http httplib::Server svr; svr.Get("/hi", [](const httplib::Request &, httplib::Response &res) { res.set_content("Hello World!", "text/plain"); }); svr.lesten("0.0.0.0",8080); </code></pre> <pre><code class="prism language-c++">#include "./cpp-httplib/httplib.h"//安装的cpp-httplib库的位置 #include "searcher.hpp" const std::string input = "./data/purify_html/purify.txt";//建立索引的数据源 const std::string root_path = "./wwwroot";//web根目录 int main() { MySearcher::Searcher searcher; searcher.InitSearcher(input); httplib::Server svr; //设置web根目录 svr.set_base_dir(root_path.c_str()); //Lambda表达式想要引用外部的对象,需要在[] 中&+对象---->[&searcher] svr.Get("/s",[&searcher](const httplib::Request& req, httplib::Response& res){ //res.set_content("hello word!","text/plain");//里面是页面的显示内容,以文本形式显示 if(!req.has_param("word"))//has_param--->判断是否有参数 { res.set_content("需要有搜索关键字!", "text/plain; charset=utf-8"); //设置返回内容"text/plain" 对应的是http中的Content-Type设置charset=utf-8的时候中间不能带空格 return ; } std::string key_word = req.get_param_value("word");//获取参数 std::cout<<"用户在搜索:"<<key_word<<std::endl; std::string json_string; searcher.Search(key_word,&json_string); //到这里就是执行完搜索服务了,需要给用户返回搜索结果---->json的Content-Type是application/json res.set_content(json_string,"application/json"); }); //将其设置为listen状态 svr.listen("0.0.0.0",8080); return 0; } </code></pre> <h3>9.编写前端模块</h3> <p><strong>了解<code>vscode</code></strong></p> <pre><code class="prism language-c++">它是一个编辑器 </code></pre> <p><strong>安装插件</strong></p> <pre><code class="prism language-c++">1.Chinese(汉化) 2.open in browser//写好的网页可以直接单击右键用浏览器打开 3.Remote -SSH //用来链接Linux 在命令行输入remote -ssh之后就开始登录了跟xshell的登录是一样的 </code></pre> <h4>编写前端代码</h4> <h5>了解html css js</h5> <pre><code>html: 是网页的骨骼---负责网页结构 css: 负责网页美观 js(javascript):网页的灵魂--网页的动态效果,前后端交互 //我是对着教程:w3cschool这个网站去写的一些前端模块 </code></pre> <h5>编写html</h5> <ol> <li> <p>div 元素是块级元素,它是可用于组合其他 HTML 元素的容器。div 元素没有特定的含义</p> </li> <li> <p>input 是设置表单数据</p> </li> <li> <p>button元素定义可点击的按钮</p> </li> <li> <p>a标签,href 属性规定链接的目标。开始标签和结束标签之间的文字被作为超级链接来显示。</p> <pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3school.com.cn/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Visit W3School<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span> <span class="token comment"><!--上面这行代码显示为:Visit W3School--></span> </code></pre> </li> <li> <pre><code class="prism language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>这个就是一个普通的标签<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>i</span><span class="token punctuation">></span></span>这个是斜体标签<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>i</span><span class="token punctuation">></span></span> </code></pre> </li> </ol> <h5>编写css</h5> <p>有很多种方法将html和css进行关联起来,这里我就直接采用style将其内联到html中</p> <pre><code class="prism language-html">设置样式的本质:找到要设置的标签,设置它的属性 1.选择特定的标签:类选择器,标签选择器,复合选择器 2.设置指定标签的属性:具体见代码(很多属性也可以去参考已有的网页他们的属性设置) <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"> <span class="token comment">/*去掉网页中的所有默认内外边距*/</span> <span class="token selector">*</span> <span class="token punctuation">{</span> <span class="token comment">/*设置外边距*/</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token comment">/*设置内边距*/</span> <span class="token property">padding</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*设置body内的内容和html的呈现是1:1的*/</span> <span class="token selector">html, body</span> <span class="token punctuation">{</span> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*.开头的一般叫类选择器*/</span> <span class="token selector">.container</span> <span class="token punctuation">{</span> <span class="token comment">/*设置div的宽度*/</span> <span class="token property">width</span><span class="token punctuation">:</span> 800px<span class="token punctuation">;</span> <span class="token comment">/*通过设置外边距达到居中效果*/</span> <span class="token property">margin</span><span class="token punctuation">:</span> 0px auto<span class="token punctuation">;</span> <span class="token comment">/* 设置外边距的上边距,保持元素和网页的上部距离 */</span> <span class="token property">margin-top</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* 复合选择器,选择container下的search */</span> <span class="token selector">.container .search</span> <span class="token punctuation">{</span> <span class="token comment">/* 宽度与父标签一致 */</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token comment">/* 高度设置为52xp */</span> <span class="token property">height</span><span class="token punctuation">:</span> 52px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* 选中input标签 单看input就是标签选择器,不需要加.*/</span> <span class="token selector">.container .search input</span> <span class="token punctuation">{</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token comment">/*给input和button设置left浮动就可以让两个盒子之间的边距清零就可以拼在一起了*/</span> <span class="token property">width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span> <span class="token comment">/* input在设置的时候没有考虑边框的问题,所以同height的情况下button会比input小一点 */</span> <span class="token comment">/* 设置边框宽度(1px),边框的样式(实线) ,边框颜色(black) */</span> <span class="token property">border</span><span class="token punctuation">:</span> 1px solid black<span class="token punctuation">;</span> <span class="token comment">/* 右边框给去除 */</span> <span class="token property">border-right</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token comment">/* 设置内边距,让默认文字不要紧贴左侧边框 */</span> <span class="token property">padding-left</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span> <span class="token comment">/* 设置input内部字体的颜色和字体大小、样式*/</span> <span class="token property">color</span><span class="token punctuation">:</span> #ccc<span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 17px<span class="token punctuation">;</span> <span class="token property">font-family</span><span class="token punctuation">:</span> Georgia<span class="token punctuation">,</span> serif<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.container .search button</span> <span class="token punctuation">{</span> <span class="token property">float</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span> <span class="token comment">/*设置button的背景颜色,可以通过f12去查看百度或者其他浏览器的颜色设置*/</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #4e6ef2<span class="token punctuation">;</span> <span class="token comment">/*设置button中的字体颜色*/</span> <span class="token property">color</span><span class="token punctuation">:</span> #FFF<span class="token punctuation">;</span> <span class="token comment">/*设置button字体大小*/</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span> <span class="token comment">/* 设置字体样式 */</span> <span class="token property">font-family</span><span class="token punctuation">:</span> Georgia<span class="token punctuation">,</span> serif<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.container .result</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token selector">.container .result .docinfo</span> <span class="token punctuation">{</span> <span class="token comment">/*上边距*/</span> <span class="token property">margin-top</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*a标签的设置,a和i属于行内元素,为了防止显示错误需要加上display:block*/</span> <span class="token selector">.container .result .docinfo a</span> <span class="token punctuation">{</span> <span class="token comment">/*设置块级元素,可以独占一行*/</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token comment">/*去除a标签的下划线*/</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token comment">/*设置a标签的字体大小,颜色*/</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 18px<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> #4e6ef2<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*a标签的光标事件*/</span> <span class="token selector">.container .result .docinfo a:hover</span> <span class="token punctuation">{</span> <span class="token comment">/*设置鼠标放在a标签上的动态效果*/</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> underline<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*p标签属性设置*/</span> <span class="token selector">.container .result .docinfo p</span> <span class="token punctuation">{</span> <span class="token comment">/*就是让p标签内容与a标签内容有点空位*/</span> <span class="token property">margin-top</span><span class="token punctuation">:</span> 3px<span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span> <span class="token property">font-family</span><span class="token punctuation">:</span> Georgia<span class="token punctuation">,</span> serif<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*i标签属性设置*/</span> <span class="token selector">.container .result .docinfo i</span> <span class="token punctuation">{</span> <span class="token comment">/*设置块级元素,可以独占一行*/</span> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span> <span class="token comment">/*取消标签斜体风格*/</span> <span class="token property">font-style</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 13<span class="token punctuation">;</span> <span class="token property">font-family</span><span class="token punctuation">:</span> Georgia<span class="token punctuation">,</span> serif<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> greenyellow<span class="token punctuation">;</span> <span class="token punctuation">}</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span> </code></pre> <h5>编写js</h5> <pre><code class="prism language-c++">直接使用原生的js成本比较高,这里我使用的是JQuery //有点像c++语言和STL库之间的关系 //这里我直接网页引入微软的CDN <head> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"></script> </head> </code></pre> <p><code>JQuery</code>中会遇到的一些函数或者用法:</p> <ul> <li> <pre><code class="prism language-js"><span class="token comment">//这样就可以提取input里面的value数据</span> <span class="token comment">//()里面填写的是要提取谁里面的数据,提input里面的value</span> len query <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">".container .search input"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre> </li> <li> <pre><code class="prism language-js">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"query = "</span> <span class="token operator">+</span>query<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//console 是浏览器的对话框,可以用来查看js数据</span> <span class="token comment">//具体可以通过F12然后选择控制器去查看</span> </code></pre> </li> <li> <pre><code class="prism language-js"><span class="token comment">//发送http请求,ajax:属于一个和后端进行数据交互的函数,JQuery中的</span> <span class="token function">ajax</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//具体的使用见下面的代码</span> </code></pre> </li> </ul> <p>还有一些的使用不好单独举例,见下面的代码注释</p> <pre><code class="prism language-js"><span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span>js代码<span class="token operator">--</span><span class="token operator">></span> <span class="token operator"><</span>script<span class="token operator">></span> <span class="token keyword">function</span> <span class="token function">Search</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//是浏览器的弹出框,当点击搜索一下会弹出alert的内容</span> <span class="token comment">//alert("hello js");</span> <span class="token comment">//1.提取数据,$可以理解成JQuery的别称</span> <span class="token comment">//()里面填写的是要提取谁里面的数据,提input里面的value</span> <span class="token keyword">let</span> query <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">".container .search input"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"query = "</span> <span class="token operator">+</span> query<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//console 是浏览器的对话框,可以用来查看js数据</span> <span class="token comment">//2.发送http请求,ajax:属于一个和后端进行数据交互的函数</span> <span class="token comment">//type是请求的方法(GET,POSE)这里用GET,url就是自己的url后面加上/s?word=query</span> <span class="token comment">//success:function(data)请求成功返回的参数就存在data中,相当于设置了一个回调</span> $<span class="token punctuation">.</span><span class="token function">ajax</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"GET"</span><span class="token punctuation">,</span> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">"/s?word="</span> <span class="token operator">+</span> query<span class="token punctuation">,</span> <span class="token function-variable function">success</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">BuildHtml</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">BuildHtml</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//想重新构建一个网页信息,data是从http_server中返回的json_string</span> <span class="token keyword">let</span> result_tag <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">".container .result"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//获取网页中的result标签</span> result_tag<span class="token punctuation">.</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//清空历史搜索结果</span> <span class="token comment">//类似于c++里面的for(auto e : result)</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">let</span> elem <span class="token keyword">of</span> data<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">let</span> a_tag <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"<a>"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">text</span><span class="token operator">:</span> elem<span class="token punctuation">.</span>title<span class="token punctuation">,</span> <span class="token comment">//text就代表标签内容</span> <span class="token literal-property property">href</span><span class="token operator">:</span> elem<span class="token punctuation">.</span>url<span class="token punctuation">,</span> <span class="token comment">//设置跳转网页的url</span> <span class="token literal-property property">target</span><span class="token operator">:</span> <span class="token string">"_blank"</span> <span class="token comment">//设置跳转,即跳转到一个新的网页中</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">let</span> p_tag <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"<p>"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">text</span><span class="token operator">:</span> elem<span class="token punctuation">.</span>desc <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">let</span> i_tag <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"<i>"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">text</span><span class="token operator">:</span> elem<span class="token punctuation">.</span>url <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">let</span> doc_tag <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"<div>"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token keyword">class</span><span class="token operator">:</span> <span class="token string">"docinfo"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> a_tag<span class="token punctuation">.</span><span class="token function">appendTo</span><span class="token punctuation">(</span>doc_tag<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//表示a_tag添加到doc_tag中</span> p_tag<span class="token punctuation">.</span><span class="token function">appendTo</span><span class="token punctuation">(</span>doc_tag<span class="token punctuation">)</span><span class="token punctuation">;</span> i_tag<span class="token punctuation">.</span><span class="token function">appendTo</span><span class="token punctuation">(</span>doc_tag<span class="token punctuation">)</span><span class="token punctuation">;</span> doc_tag<span class="token punctuation">.</span><span class="token function">appendTo</span><span class="token punctuation">(</span>result_tag<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//doc_tag要添加到result标签中</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span> </code></pre> <h3>10.细节优化</h3> <h4>文档重复显示问题</h4> <p>首先是之前在searcher.hpp中有一处的问题具体暴露出来就是以下问题,当一个文档同时出现几个关键词时,会重复的将其打印出来<a href="http://img.e-com-net.com/image/info8/a7d30d839c0245e88db7b0deda4ff76e.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/a7d30d839c0245e88db7b0deda4ff76e.jpg" alt="Boost搜索引擎的实现_第4张图片" width="650" height="231" style="border:1px solid black;"></a><a href="http://img.e-com-net.com/image/info8/ccb74a3b587949ea9732650e683f0fba.jpg" target="_blank"><img src="http://img.e-com-net.com/image/info8/ccb74a3b587949ea9732650e683f0fba.jpg" alt="Boost搜索引擎的实现_第5张图片" width="650" height="380" style="border:1px solid black;"></a></p> <p>接下来就是去优化这段代码,其实也很简单,我这里选择重新定义一个类</p> <pre><code class="prism language-c++">struct DedupElem { uint64_t doc_id; int wight;//权值进行加和 vector<string> key_word;//存放一组关键字 }; </code></pre> <p>具体实现见下面代码:</p> <pre><code class="prism language-c++">void Search(const std::string &Inquire, std::string *json_string) { std::vector<std::string> words; MyTool::Cppjieba::CutForSearch(Inquire, &words); //-------------------------begin--------------------------------- 这是需要的两个变量 std::unordered_map<uint64_t, DedupElem> dedup_map; // 建立一个文档id与Dedupmlem的映射 std::vector<DedupElem> list_all;//用来存dedup_map中的second //--------------------------end-------------------------------- for (std::string key_word : words) { boost::to_lower(key_word); MyIndex::InvertedList *inverted_list = _index->GetInverted(key_word); if (inverted_list == nullptr) { continue; } //------------------------------begin-----------------------------------------这之间的就是优化的代码 // 优化代码,遍历InvertedList for (const auto &elem : *inverted_list) { auto &item = dedup_map[elem.file_id]; // 如果文档id没有就构建,有了就返回second的引用 item.doc_id = elem.file_id; // 构建文档id item.weight += elem.weight; // 将关键字的权重全部相加 item.key_words.push_back(elem.key_word); // 同一文档的关键字全部插入到vector中 } } for (auto &elem : dedup_map) { list_all.push_back(std::move(elem.second)); } //-------------------------------end-------------------------------------------到这结束 std::sort(list_all.begin(), list_all.end(), Compare()); Json::Value root; for (auto &elem : list_all) { // 根据result里面的数据,去正排查找文档并且把查找到的文档序列化 MyIndex::Docinfo *doc = _index->GetForward(elem.doc_id); Json::Value tmp; tmp["title"] = doc->title; // 标题 tmp["desc"] = GetDesc(doc->content, elem.key_words[0]); // 描述--->这里就取vector<string>里面的第一个词作为关键字了 tmp["url"] = doc->url; // 要跳转的网页 root.append(tmp); } *json_string = Json::FastWriter().write(root); } </code></pre> <h4>添加一些日志信息</h4> <p>具体见代码,这个主要目的就是为了看着方便,只要是之前代码用<code>cerr</code>或者<code>cout</code>打印的都可以换成LOG();</p> <pre><code class="prism language-c++">#pragma once #include<iostream> #include<string> #include<cstring> #include<vector> #include<ctime> #define NORMAL 0 //正常 #define WARNING 1 //警告 #define DEBUG 3 //调试 #define CRITICAL 4 //严重错误 #define LOG(STATE,MESSAGE) log(#STATE,MESSAGE,__FILE__,__LINE__) void log(const std::string &state,const std::string& message,const std::string& file,int line) { //看日志的话一般看处于什么状态是正常还是错误以及输入的信息,其次看什么文件出问题了,在哪一行 time_t t = time(nullptr);//获取当前的时间戳 //下面是将时间戳转换成北京时间 //struct tm *gmtime(const time_t *timep); struct tm* my_tm = gmtime(&t); //因为有时差,所以小时需要加上8 my_tm->tm_hour+=8; std::string format = "%Y-%m-%d:%H:%M:%S"; //size_t strftime(char *s, size_t max, const char *format,const struct tm *tm); //char* s 输出型参数 //size_t max 是s的大小 //format就是要按照什么形式输出 char buff[25]; memset(buff,0,sizeof(buff)); strftime(buff,sizeof(buff),format.c_str(),my_tm); std::cout<<"["<<state<<"]"<<"["<< buff <<"]" <<"[" << message <<"]"<<"["<<file<<"]"<<"["<< line <<"]"<<std::endl; } </code></pre> <h4>添加去暂停词代码</h4> <pre><code class="prism language-c++"> 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"; class Cppjieba { private: // 增加一个去暂停词的功能---->这个功能会让创建变的非常的慢 cppjieba::Jieba jieba; std::unordered_map<std::string, bool> stop_word; // 同时这些只需要同时存在一份,所以直接设置为单例 static Cppjieba *singloten; private: Cppjieba() : jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_PATH, STOP_WORD_PATH){} Cppjieba(const Cppjieba &ba) = delete; Cppjieba &operator=(const Cppjieba &ba) = delete; public: static Cppjieba *GetJieba() // 获取单例 { static std::mutex mtx; if (singloten == nullptr) { mtx.lock(); if (singloten == nullptr) { singloten = new Cppjieba(); singloten->InitJieba(); // 构建完进行初始化 } mtx.unlock(); } return singloten; } bool InitJieba() { std::ifstream in(STOP_WORD_PATH); if (!in.is_open()) { std::string st = "open file error:"; LOG(CRITICAL, st += STOP_WORD_PATH); return false; } std::string line; while (std::getline(in, line)) { stop_word[line] = true; // 插入暂停词到stop_word中 } in.close(); return true; } void CutForSearchHelper(const std::string &s, std::vector<std::string> *words) { jieba.CutForSearch(s, *words); for (auto item = words->begin(); item != words->end();) // 就是遍历words然后里面的每个元素都去stop_find里面去找 { auto it = stop_word.find(*item); if (it != stop_word.end()) { // item是暂停词需要去除,同时需要考虑迭代器失效问题 item = words->erase(item); } else { item++; } } } public: static void CutForSearch(const std::string &s, std::vector<std::string> *words) { MyTool::Cppjieba::GetJieba()->CutForSearchHelper(s, words); // 直接调用上面的分词助手 // 好处就是无需修改其他的代码 } }; Cppjieba *Cppjieba::singloten = nullptr; }; </code></pre> <h3>11.将其部署到云服务器上</h3> <h4>部署</h4> <p>部署其实很简单</p> <pre><code class="prism language-shell">将日志信息输出到log.txt中 ,把标准错误重定向到标准输出 最后的<span class="token operator">&</span>就是以守护进程的方式 ---这样即使关闭xshell,这个服务依旧是存在的 <span class="token function">nohup</span> ./Http_Server <span class="token operator">></span> log.txt <span class="token operator"><span class="token file-descriptor important">2</span>></span><span class="token file-descriptor important">&1</span> <span class="token operator">&</span> 如果想要关闭服务,可以通过ps axj <span class="token operator">|</span> <span class="token function">grep</span> Http_Server 查看pid然后通过 kill命令将其关闭 </code></pre> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1705977245878792192"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(项目笔记,搜索引擎,c++,STL,linux,正排/倒排索引)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1904071810454974464.htm" title="视频格式批量转换工具-FFGO" target="_blank">视频格式批量转换工具-FFGO</a> <span class="text-muted">屠屠在干嘛</span> <a class="tag" taget="_blank" href="/search/FFGO/1.htm">FFGO</a><a class="tag" taget="_blank" href="/search/%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%8E%82/1.htm">格式工厂</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91/1.htm">视频</a> <div>由于毕设需要webm来展示动画而搜索引擎所有的webm转换工具都是在线且限制转换大小的就算大小刚好也容易报错甚至转换不出来绞尽脑汁干脆自己写了一个视频格式转换工具基本上视频格式都能够支持,如果后续有什么无法支持的格式我会后续继续更新所以暂且命名他为FF-GO吧也挺好听的,下面是软件的截图和下载链接下载直链:https://tuwp.cc:999/d/LOVETU/%E5%AE%9E%E7%94%A</div> </li> <li><a href="/article/1904063111523004416.htm" title="文件的基本的基本属性" target="_blank">文件的基本的基本属性</a> <span class="text-muted">伶星37</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a> <div>为什么要有基本属性Linux系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限。为了保护系统的安全性,Linux系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定。例子你可以把Linux比作成一个学校,里面的人学生老师校长里面的资料课本学校档案老师个人备案资料学生只能看课本,其他的都不能看,而老师,可以看老师备案资料和课本。校长上面都可以看。在Linux中我们通常使</div> </li> <li><a href="/article/1904061851474391040.htm" title="Not enough information to list image symbols. Not enough information to list load addresses in ..." target="_blank">Not enough information to list image symbols. Not enough information to list load addresses in ...</a> <span class="text-muted">Water_Sounds</span> <a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">学习笔记</a><a class="tag" taget="_blank" href="/search/keil/1.htm">keil</a><a class="tag" taget="_blank" href="/search/mdk/1.htm">mdk</a> <div>除了绝大部分网上给的解决方法外:Notenoughinformationtolistimagesymbols.Notenoughinformationtolistloadaddressesin…我在向正点原子例程“输入捕获”中添加lcd驱动程序时,发现按照上述链接的做法填了路径什么的,还是报错,最后发现是这个.c文件文件没有添加进来导致这两句话一直是无定义,填进来就好了。</div> </li> <li><a href="/article/1904059330748018688.htm" title="C++开发内存监控工具推荐" target="_blank">C++开发内存监控工具推荐</a> <span class="text-muted">点云SLAM</span> <a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/1.htm">开发工具</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/1.htm">开发环境</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/AddProperty/1.htm">AddProperty</a><a class="tag" taget="_blank" href="/search/gperftools/1.htm">gperftools</a><a class="tag" taget="_blank" href="/search/Address/1.htm">Address</a><a class="tag" taget="_blank" href="/search/%E5%86%85%E5%AD%98%E7%9B%91%E6%8E%A7/1.htm">内存监控</a><a class="tag" taget="_blank" href="/search/%E8%AE%BF%E9%97%AE%E8%B6%8A%E7%95%8C/1.htm">访问越界</a> <div>在C++开发中,内存管理是至关重要的,尤其是当程序处理大数据或长时间运行时,内存泄漏或不当使用可能导致性能下降或崩溃。以下是几种常见且有效的内存监控工具,它们可以帮助开发者实时分析、诊断和优化程序的内存使用。1.ValgrindValgrind是一个广泛使用的内存调试和性能分析工具,它的Memcheck工具可以帮助你检查程序中的内存泄漏、内存越界、未初始化内存使用等问题。特点:检测内存泄漏。检查内</div> </li> <li><a href="/article/1904054528894889984.htm" title="ERROR: Failed building wheel for pyaudioFailed to build pyaudioERROR: ERROR: Failed to build insta" target="_blank">ERROR: Failed building wheel for pyaudioFailed to build pyaudioERROR: ERROR: Failed to build insta</a> <span class="text-muted">小李飞刀李寻欢</span> <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/audio/1.htm">audio</a><a class="tag" taget="_blank" href="/search/pyaudio/1.htm">pyaudio</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E8%A3%85%E5%BA%93/1.htm">安装库</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>ERROR:FailedbuildingwheelforpyaudioFailedtobuildpyaudioERROR:ERROR:Failedtobuildinstallablewheelsforsomepyproject.tomlbasedprojects(pyaudio)这个错误表明在编译pyaudio时缺少PortAudio开发库。以下是完整解决方案:Linux系统解决方案#1.安装系统</div> </li> <li><a href="/article/1904051378020478976.htm" title="服务器负载均衡" target="_blank">服务器负载均衡</a> <span class="text-muted">冬冬小圆帽</span> <a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/1.htm">负载均衡</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a> <div>1.安装EPEL仓库EPEL(ExtraPackagesforEnterpriseLinux)仓库提供了额外的软件包,安装HAProxy前需要先启用EPEL仓库。sudoyuminstallepel-release-y2.安装HAProxy通过EPEL仓库安装HAProxy。sudoyuminstallhaproxy-y注意:如果服务器上已安装Docker,可能会干扰HAProxy的安装。建议先关</div> </li> <li><a href="/article/1904045580884176896.htm" title="禁止搜索引擎收录网站内容,百度,谷歌,所有等..." target="_blank">禁止搜索引擎收录网站内容,百度,谷歌,所有等...</a> <span class="text-muted">wangxingps</span> <a class="tag" taget="_blank" href="/search/seo/1.htm">seo</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a><a class="tag" taget="_blank" href="/search/%E7%99%BE%E5%BA%A6/1.htm">百度</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a> <div>第一种、robots.txt方法搜索引擎默认的遵守robots.txt协议,创建robots.txt文本文件放至网站根目录下,编辑代码如下:User-agent:*Disallow:/通过以上代码,即可告诉搜索引擎不要抓取采取收录本网站,注意慎用如上代码:这将禁止所有搜索引擎访问网站的任何部分。如何只禁止百度搜索引擎收录抓取网页1、编辑robots.txt文件,设计标记为:User-agent:B</div> </li> <li><a href="/article/1904045579843989504.htm" title="Elasticsearch 搜索引擎原理与实践" target="_blank">Elasticsearch 搜索引擎原理与实践</a> <span class="text-muted">AI天才研究院</span> <a class="tag" taget="_blank" href="/search/Python%E5%AE%9E%E6%88%98/1.htm">Python实战</a><a class="tag" taget="_blank" href="/search/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/1.htm">自然语言处理</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/1.htm">语言模型</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E5%AE%9E%E8%B7%B5/1.htm">编程实践</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/1.htm">架构设计</a> <div>作者:禅与计算机程序设计艺术1.简介Elasticsearch是开源分布式搜索引擎,提供搜素、分析、数据可视化等功能。它是一个基于Lucene的全文搜索服务器,能够把结构化或非结构化的数据经过索引生成一个索引库,使其可以被搜索到。在现代Web应用中,搜索功能已经成为不可或缺的一项功能。但是传统上,传统搜索方式需要依赖于数据库查询或者其他复杂的查询接口。而Elasticsearch提供了一种高效、稳</div> </li> <li><a href="/article/1904043310817800192.htm" title="Centos Redis安装与配置指南" target="_blank">Centos Redis安装与配置指南</a> <span class="text-muted">程序~阿呆</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/1.htm">开发工具</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E9%97%B4%E4%BB%B6/1.htm">中间件</a> <div>1.环境说明centos7.9redis6.2.6安装方式:外网服务器可以使用wget来下载安装,内网服务器只能windows下载后上传到linux服务器上2.新建下载目录将下载的Redis放在本文件夹中mkdir/tools3.Redis下载服务器执行wget命令:wgethttp://download.redis.io/releases/redis-6.2.6.tar.gz4.解压和编译依次执</div> </li> <li><a href="/article/1904034106577055744.htm" title="OpenRAND可重复的随机数生成库" target="_blank">OpenRAND可重复的随机数生成库</a> <span class="text-muted">novanova2009</span> <a class="tag" taget="_blank" href="/search/elasticsearch/1.htm">elasticsearch</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a> <div>OpenRAND是一个C++库,旨在通过提供强大且可复制的随机数生成解决方案来促进可重复的科学研究。它是一个简单的仅头文件库,性能可移植,统计稳健,并且易于集成到任何HPC计算项目中。特征跨平台支持:OpenRAND旨在跨各种平台无缝工作,包括CPU和GPU。其仅标题库设计使其能够轻松集成到您的项目中。用户友好的API:OpenRAND提供了一个用户友好的API,可以直接在您的应用程序中生成随机数</div> </li> <li><a href="/article/1904014930978336768.htm" title="cifs挂载 mount ubuntu_在Linux上使用CIFS,如何挂载Windows共享" target="_blank">cifs挂载 mount ubuntu_在Linux上使用CIFS,如何挂载Windows共享</a> <span class="text-muted">王小约</span> <a class="tag" taget="_blank" href="/search/cifs%E6%8C%82%E8%BD%BD/1.htm">cifs挂载</a><a class="tag" taget="_blank" href="/search/mount/1.htm">mount</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a> <div>在Linux和UNIX操作系统上,可以使用mount命令的cifs选项将Windows共享安装在本地目录。常见的Internet文件系统(CIFS)是网络文件共享协议,CIFS是SMB的一种形式。在本教程中,解释如何在Windows共享上手动和自动挂载Linux系统。安装CIFS程序包要在Windows系统上挂载Linux共享,首先需要安装CIFS程序包。在Ubuntu和Debian上安装CIFS</div> </li> <li><a href="/article/1904014048387723264.htm" title="专业课笔记——(第一章:C、C++基础知识)" target="_blank">专业课笔记——(第一章:C、C++基础知识)</a> <span class="text-muted">大小胖虎</span> <a class="tag" taget="_blank" href="/search/C%2FC%2B%2B%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E7%AC%94%E8%AE%B0/1.htm">C/C++基础知识笔记</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/C/1.htm">C</a><a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/1.htm">数据类型</a><a class="tag" taget="_blank" href="/search/%E6%93%8D%E4%BD%9C%E7%B1%BB%E5%9E%8B/1.htm">操作类型</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a> <div>目录一、数据类型二、不同格式输出的含义三、运算符优先级四、计算机基础知识五、零碎基础知识点一、数据类型1、C语言中的最简单的数据类型:整数类型、字符类型、浮点类型(C语言没有逻辑型(bool)它是C++特有的,而c语言它是通过0、1表示实现的)构造类型:枚举型、数组类型、结构体类型、共用体类型、类类型(C++特有)2、计算字符串长度:strlen():c语言中的函数length():c++中的函数</div> </li> <li><a href="/article/1904013670099251200.htm" title="Linux中挂载Windows Samba共享的指南" target="_blank">Linux中挂载Windows Samba共享的指南</a> <span class="text-muted">执剑走天涯xp</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a> <div>主要步骤:安装cifs-utils确保你的Linux系统已安装cifs-utils包。如果未安装,使用以下命令:sudoapt-getinstallcifs-utils#Debian/Ubuntu系统sudoyuminstallcifs-utils#CentOS/RHEL系统创建挂载点创建一个本地目录来挂载Windows共享:sudomkdir/mnt/share编辑/etc/fstab文件使用文</div> </li> <li><a href="/article/1904012278655676416.htm" title="嵌入式Linux网络编程实战:基于libcurl实现Gitee文件上传" target="_blank">嵌入式Linux网络编程实战:基于libcurl实现Gitee文件上传</a> <span class="text-muted">银河码</span> <a class="tag" taget="_blank" href="/search/Linux%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/1.htm">Linux网络编程</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/gitee/1.htm">gitee</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a><a class="tag" taget="_blank" href="/search/vscode/1.htm">vscode</a><a class="tag" taget="_blank" href="/search/tcp%2Fip/1.htm">tcp/ip</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a> <div>嵌入式Linux网络编程实战:基于libcurl实现Gitee文件上传【本文代码已在立创·泰山派平台验证通过,可直接用于物联网设备数据上报场景】一、功能概述与实现效果1.1核心功能本地文件读取:支持任意二进制/文本文件Base64编码转换:符合RFC4648标准HTTP传输:通过libcurl实现,也可以使用HTTPS加密通信GiteeAPI对接:自动创建/更新仓库文件1.2运行效果演示#上传本地</div> </li> <li><a href="/article/1904006229223600128.htm" title="linux如何释放内存缓存" target="_blank">linux如何释放内存缓存</a> <span class="text-muted">千航@abc</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E7%BC%93%E5%AD%98/1.htm">缓存</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E5%86%85%E5%AD%98/1.htm">内存</a> <div>[root@redis~]#sync#将内存缓存数据强制写入磁盘(保存数据后再做释放)[root@redis~]#echo1>/proc/sys/vm/drop_caches#释放内存缓存</div> </li> <li><a href="/article/1904005977171095552.htm" title="ubuntu 20.04安装visual studio code并配置C++编译环境" target="_blank">ubuntu 20.04安装visual studio code并配置C++编译环境</a> <span class="text-muted">Android Coder</span> <a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/NDK%E4%B8%8E%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">NDK与音视频</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a> <div>1.下载安装visualstudiocode我的系统是Ubuntu20.04,首先是下载安装包。进入官网,直接下载压缩包。https://code.visualstudio.com/Download下载完成后双击安装即可。2.C++运行环境配置插件的安装汉化:过于简单,直接按照教程操作:https://jingyan.baidu.com/article/7e44095377c9d12fc1e2ef</div> </li> <li><a href="/article/1904001817981808640.htm" title="Linux:进程间通信——信号" target="_blank">Linux:进程间通信——信号</a> <span class="text-muted">muke_r</span> <a class="tag" taget="_blank" href="/search/1024%E7%A8%8B%E5%BA%8F%E5%91%98%E8%8A%82/1.htm">1024程序员节</a> <div>信号是UNIX和Linux系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。信号是软中断,通常信号是由一个错误产生的。但它们还可以作为进程间通信或修改行为的一种方式,明确地由一个进程发送给另一个进程目录一、信号种类1.常见的信号2.不可靠信号和可靠信号注意二、信号捕捉三、进程休眠号四、信号集和信号阻塞五、附带数据信息的信号处理一、信号种类在终端输入kill-l命令可以看到l</div> </li> <li><a href="/article/1903998541542518784.htm" title="linux——线程" target="_blank">linux——线程</a> <span class="text-muted">這~悸ベ雨落憂殇</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a> <div>线程概念什么是线程?在一个程序里的一个执行流叫做线程。一切进程至少有一个线程线程在进程内部运行,本质是在进程地址空间内运行在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化我们都知道在每一个进程都有属于自己的PCB,里面装满了描述进程的各种字段…,而线程呢,是在进程中产生的,所以会共享共一个进程地址空间,如上图所示。线程的优点创建一个新线程的代价要比创建一个新进程小与进程之间</div> </li> <li><a href="/article/1903995513812873216.htm" title="Visual Studio Code官网下载地址及使用技巧(含常用的拓展插件推荐)" target="_blank">Visual Studio Code官网下载地址及使用技巧(含常用的拓展插件推荐)</a> <span class="text-muted">ITCTCSDN</span> <a class="tag" taget="_blank" href="/search/vscode/1.htm">vscode</a><a class="tag" taget="_blank" href="/search/ide/1.htm">ide</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E8%BE%91%E5%99%A8/1.htm">编辑器</a> <div>VisualStudioCode(简称“VSCode”)是Microsoft于2015年4月发布的可运行于MacOS、Windows和Linux之上的跨平台源代码编辑器,它具有对JavaScript,TypeScript和Node.js的内置支持,并具有丰富的其他语言(例如C++,C#,Java,Python,PHP,Go)和运行时(例如.NET和Unity)扩展的生态系统。VisualStudi</div> </li> <li><a href="/article/1903992863927103488.htm" title="Linux:动静态库" target="_blank">Linux:动静态库</a> <span class="text-muted">嶔某</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a> <div>✨✨所属专栏:Linux✨✨✨✨作者主页:嶔某✨✨什么是库库是写好的现有的,成熟的可以复用的代码。现实中每个程序都需要依赖很多基础的底层库。世界上有很多大佬为了实现某一个功能,写了很多很NB的代码。他们把代码封装成一个库,这样我们不必写出像他们一样厉害的代码,只需要使用它们分享的库,也能使用对应的功能了。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。静态库.a[Linux/</div> </li> <li><a href="/article/1903992861863505920.htm" title="Linux:编辑器Vim和Makefile" target="_blank">Linux:编辑器Vim和Makefile</a> <span class="text-muted">嶔某</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E8%BE%91%E5%99%A8/1.htm">编辑器</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a> <div>✨✨所属专栏:Linux✨✨✨✨作者主页:嶔某✨✨vim的三种常用模式分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode)各模式的功能区分如下:正常/普通/命令模式(Normalmode)控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入Insertmode下,或者到lastlinemode。插入模式(Insertmode)只有</div> </li> <li><a href="/article/1903989450153717760.htm" title="算法笔记——前缀树、贪心算法(更新ing......." target="_blank">算法笔记——前缀树、贪心算法(更新ing.......</a> <span class="text-muted">不吃香菜的码农</span> <a class="tag" taget="_blank" href="/search/%E5%B7%A6%E7%A5%9E%E7%AE%97%E6%B3%95%E7%AC%94%E8%AE%B0/1.htm">左神算法笔记</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95/1.htm">贪心算法</a><a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E5%A0%86%E6%A0%88/1.htm">堆栈</a> <div>前缀树、贪心算法一、前缀树1.什么是前缀树2.如何生成前缀树二、贪心算法1.拼接字符串2.金条问题3.项目会议时间问题4.项目收益最大化4.随时获得数据流的中位数一、前缀树1.什么是前缀树前缀树一般指字典树这是指一种结构而不是一类题(注意信息是在树的路上)典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查</div> </li> <li><a href="/article/1903987433905647616.htm" title="【AI大模型】搭建本地大模型GPT-NeoX:详细步骤及常见问题处理" target="_blank">【AI大模型】搭建本地大模型GPT-NeoX:详细步骤及常见问题处理</a> <span class="text-muted">qzw1210</span> <a class="tag" taget="_blank" href="/search/gpt/1.htm">gpt</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a> <div>搭建本地大模型GPT-NeoX:详细步骤及常见问题处理GPT-NeoX是一个开源的大型语言模型框架,由EleutherAI开发,可用于训练和部署类似GPT-3的大型语言模型。本指南将详细介绍如何在本地环境中搭建GPT-NeoX,并解决过程中可能遇到的常见问题。1.系统要求1.1硬件要求1.2软件要求操作系统:Linux(推荐Ubuntu20.04或更高版本)CUDA:11.2或更高版本Python</div> </li> <li><a href="/article/1903986677349675008.htm" title="C++函数返回多个值:结构体、tuple" target="_blank">C++函数返回多个值:结构体、tuple</a> <span class="text-muted">@you_123</span> <a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a> <div>C++函数一般可以返回一个值,但是在使用中常常需要一个函数返回多个值,因此可以使用结构体或tuple来进行实现。注意看代码里的注释!!!1.使用结构体返回多个值实现步骤:1.先定义一个结构体2.准备我们要实现的函数(需要返回多个值)3.在要实现的函数内调用结构体返回多个值4.使用函数返回结果代码示例:step1:定义结构体structPointStruct{floatwithout_floor;i</div> </li> <li><a href="/article/1903984535553503232.htm" title="基于 C++ 类的程序设计模式与应用研究" target="_blank">基于 C++ 类的程序设计模式与应用研究</a> <span class="text-muted">饼干帅成渣</span> <a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>摘要C++语言凭借其强大的功能在软件开发领域占据重要地位,类作为C++面向对象编程的核心,承载着数据封装、代码复用等关键使命。本文深入剖析C++类的基础概念、核心特性及其在实际编程中的应用。通过详细阐述类的定义、成员构成、访问控制以及封装、继承、多态等特性,结合具体代码示例展示其在构建软件架构中的作用。同时,探讨C++类在应用中面临的常见问题及解决方案,为开发者高效运用C++类进行程序设计提供有力</div> </li> <li><a href="/article/1903983902607863808.htm" title="c++测试题" target="_blank">c++测试题</a> <span class="text-muted">Helibo44</span> <a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>题目A题目描述:给定两个非负整数A和B,以字符串形式输入,计算A*B的结果,并以字符串形式输出。输入的整数长度不超过1000位。输入格式:第一行,包含一个字符串A。第二行,包含一个字符串B。输出格式:输出一个字符串,表示A×B的结果。样例:输入:123456输出:56088样例解释:123*456=56088。题目B题目描述:给定一个主字符串S和一个模式字符串T,在主字符串中找到所有模式字符串的出</div> </li> <li><a href="/article/1903983524050956288.htm" title="第十二届蓝桥杯C++青少年组中/高级组省赛2021年真题解析" target="_blank">第十二届蓝桥杯C++青少年组中/高级组省赛2021年真题解析</a> <span class="text-muted">码农StayUp</span> <a class="tag" taget="_blank" href="/search/C%2B%2B%E8%93%9D%E6%A1%A5%E6%9D%AF%E9%9D%92%E5%B0%91%E5%B9%B4%E7%BB%84%E7%9C%9F%E9%A2%98%E8%A7%A3%E6%9E%90/1.htm">C++蓝桥杯青少年组真题解析</a><a class="tag" taget="_blank" href="/search/%E8%93%9D%E6%A1%A5%E6%9D%AF/1.htm">蓝桥杯</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div>一、单选题第1题下列符号中哪个在C++中表示行注释()。A:!B:#C:]D://答案:D在C++中,行注释的表示方式是使用双斜杠//。行注释是指从双斜杠开始直到该行的末尾,所有内容都会被编译器忽略,不会被编译和执行。第2题每个C++程序都必须有且仅有一个()A:函数B:预处理命令C:主函数D:语句答案:C每个C++程序都必须有且仅有一个主函数。第3题下列字特串中不可以用作C++变量名称的是()A</div> </li> <li><a href="/article/1903981379700453376.htm" title="Linux 常用命令 - last 【显示历史登录用户列表】" target="_blank">Linux 常用命令 - last 【显示历史登录用户列表】</a> <span class="text-muted">WKJay_</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/1.htm">常用命令</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a> <div>简介last命令源自英文单词“last”,意为“最后”。该命令用于显示系统中用户的登录和注销记录,以及系统的重启和关机记录。它通过读取/var/log/wtmp文件来获取这些信息,wtmp文件记录了所有用户的登录和注销活动。使用方式last[options][username...][tty...]lastb[options][username...][tty...]常用选项-a,--hostla</div> </li> <li><a href="/article/1903978852259000320.htm" title="【C/C++】在排序数组中查找元素的第一个和最后一个位置(leetcode T34)" target="_blank">【C/C++】在排序数组中查找元素的第一个和最后一个位置(leetcode T34)</a> <span class="text-muted">勇士小蓝0727</span> <a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E8%93%9D%E6%A1%A5%E6%9D%AF/1.htm">蓝桥杯</a> <div>核心考点:法一双指针法;法二二分查找法题目描述:给你一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回[-1,-1]。你必须设计并实现时间复杂度为O(logn)的算法解决此问题。(示例见文末)答案详解:方法一:双指针法vectorsearchRange(vector&nums,inttarge</div> </li> <li><a href="/article/1903973553884164096.htm" title="从 Windows 共享到 Linux:Jenkins 代码部署方案调整" target="_blank">从 Windows 共享到 Linux:Jenkins 代码部署方案调整</a> <span class="text-muted">XMYX-0</span> <a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/jenkins/1.htm">jenkins</a> <div>文章目录从Windows共享到Linux:Jenkins代码部署方案调整方案1:使用NFS(推荐)介绍特点适用场景在192.168.1.100上配置NFS安装NFS服务器创建共享目录修改NFS共享配置启动NFS并应用配置在controller服务器上挂载NFS安装NFS客户端手动挂载开机自动挂载方案2:使用Rsync(RemoteSync)同步文件介绍特点适用场景在controller服务器上安装</div> </li> <li><a href="/article/49.htm" title="辗转相处求最大公约数" target="_blank">辗转相处求最大公约数</a> <span class="text-muted">沐刃青蛟</span> <a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a><a class="tag" taget="_blank" href="/search/%E6%BC%8F%E6%B4%9E/1.htm">漏洞</a> <div>无言面对”江东父老“了,接触编程一年了,今天发现还不会辗转相除法求最大公约数。惭愧惭愧!   为此,总结一下以方便日后忘了好查找。   1.输入要比较的两个数a,b   忽略:2.比较大小(因为后面要的是大的数对小的数做%操作)   3.辗转相除(用循环不停的取余,如a%b,直至b=0)   4.最后的a为两数的最大公约数 &</div> </li> <li><a href="/article/176.htm" title="F5负载均衡会话保持技术及原理技术白皮书" target="_blank">F5负载均衡会话保持技术及原理技术白皮书</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/F5/1.htm">F5</a><a class="tag" taget="_blank" href="/search/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/1.htm">负载均衡</a> <div>一.什么是会话保持?        在大多数电子商务的应用系统或者需要进行用户身份认证的在线系统中,一个客户与服务器经常经过好几次的交互过程才能完成一笔交易或者是一个请求的完成。由于这几次交互过程是密切相关的,服务器在进行这些交互过程的某一个交互步骤时,往往需要了解上一次交互过程的处理结果,或者上几步的交互过程结果,服务器进行下</div> </li> <li><a href="/article/303.htm" title="Object.equals方法:重载还是覆盖" target="_blank">Object.equals方法:重载还是覆盖</a> <span class="text-muted">Cwind</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/generics/1.htm">generics</a><a class="tag" taget="_blank" href="/search/override/1.htm">override</a><a class="tag" taget="_blank" href="/search/overload/1.htm">overload</a> <div>本文译自StackOverflow上对此问题的讨论。 原问题链接   在阅读Joshua Bloch的《Effective Java(第二版)》第8条“覆盖equals时请遵守通用约定”时对如下论述有疑问: “不要将equals声明中的Object对象替换为其他的类型。程序员编写出下面这样的equals方法并不鲜见,这会使程序员花上数个小时都搞不清它为什么不能正常工作:” pu</div> </li> <li><a href="/article/430.htm" title="初始线程" target="_blank">初始线程</a> <span class="text-muted">15700786134</span> <div>      暑假学习的第一课是讲线程,任务是是界面上的一条线运动起来。            既然是在界面上,那必定得先有一个界面,所以第一步就是,自己的类继承JAVA中的JFrame,在新建的类中写一个界面,代码如下: public class ShapeFr</div> </li> <li><a href="/article/557.htm" title="Linux的tcpdump" target="_blank">Linux的tcpdump</a> <span class="text-muted">被触发</span> <a class="tag" taget="_blank" href="/search/tcpdump/1.htm">tcpdump</a> <div>用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具。 tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支 持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。 实用命令实例 默认启动 tcpdump 普通情况下,直</div> </li> <li><a href="/article/684.htm" title="安卓程序listview优化后还是卡顿" target="_blank">安卓程序listview优化后还是卡顿</a> <span class="text-muted">肆无忌惮_</span> <a class="tag" taget="_blank" href="/search/ListView/1.htm">ListView</a> <div>最近用eclipse开发一个安卓app,listview使用baseadapter,里面有一个ImageView和两个TextView。使用了Holder内部类进行优化了还是很卡顿。后来发现是图片资源的问题。把一张分辨率高的图片放在了drawable-mdpi文件夹下,当我在每个item中显示,他都要进行缩放,导致很卡顿。解决办法是把这个高分辨率图片放到drawable-xxhdpi下。 &nb</div> </li> <li><a href="/article/811.htm" title="扩展easyUI tab控件,添加加载遮罩效果" target="_blank">扩展easyUI tab控件,添加加载遮罩效果</a> <span class="text-muted">知了ing</span> <a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a> <div>(function () { $.extend($.fn.tabs.methods, { //显示遮罩 loading: function (jq, msg) { return jq.each(function () { var panel = $(this).tabs(&</div> </li> <li><a href="/article/938.htm" title="gradle上传jar到nexus" target="_blank">gradle上传jar到nexus</a> <span class="text-muted">矮蛋蛋</span> <a class="tag" taget="_blank" href="/search/gradle/1.htm">gradle</a> <div>原文地址: https://docs.gradle.org/current/userguide/maven_plugin.html configurations {     deployerJars } dependencies {     deployerJars "org.apache.maven.wagon</div> </li> <li><a href="/article/1065.htm" title="千万条数据外网导入数据库的解决方案。" target="_blank">千万条数据外网导入数据库的解决方案。</a> <span class="text-muted">alleni123</span> <a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a> <div>从某网上爬了数千万的数据,存在文本中。 然后要导入mysql数据库。 悲剧的是数据库和我存数据的服务器不在一个内网里面。。 ping了一下, 19ms的延迟。 于是下面的代码是没用的。 ps = con.prepareStatement(sql); ps.setString(1, info.getYear())............; ps.exec</div> </li> <li><a href="/article/1192.htm" title="JAVA IO InputStreamReader和OutputStreamReader" target="_blank">JAVA IO InputStreamReader和OutputStreamReader</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/JAVA.io%E6%93%8D%E4%BD%9C+%E5%AD%97%E7%AC%A6%E6%B5%81/1.htm">JAVA.io操作 字符流</a> <div>这是第三篇关于java.io的文章了,从开始对io的不了解-->熟悉--->模糊,是这几天来对文件操作中最大的感受,本来自己认为的熟悉了的,刚刚在回想起前面学的好像又不是很清晰了,模糊对我现在或许是最好的鼓励 我会更加的去学 加油!: JAVA的API提供了另外一种数据保存途径,使用字符流来保存的,字符流只能保存字符形式的流   字节流和字符的难点:a,怎么将读到的数据</div> </li> <li><a href="/article/1319.htm" title="MO、MT解读" target="_blank">MO、MT解读</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/GSM/1.htm">GSM</a> <div>MO= Mobile originate,上行,即用户上发给SP的信息。MT= Mobile Terminate,下行,即SP端下发给用户的信息; 上行:mo提交短信到短信中心下行:mt短信中心向特定的用户转发短信,你的短信是这样的,你所提交的短信,投递的地址是短信中心。短信中心收到你的短信后,存储转发,转发的时候就会根据你填写的接收方号码寻找路由,下发。在彩信领域是一样的道理。下行业务:由SP</div> </li> <li><a href="/article/1446.htm" title="五个JavaScript基础问题" target="_blank">五个JavaScript基础问题</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/call/1.htm">call</a><a class="tag" taget="_blank" href="/search/apply/1.htm">apply</a><a class="tag" taget="_blank" href="/search/this/1.htm">this</a><a class="tag" taget="_blank" href="/search/Hoisting/1.htm">Hoisting</a> <div>下面是五个关于前端相关的基础问题,但却很能体现JavaScript的基本功底。 问题1:Scope作用范围 考虑下面的代码:  (function() { var a = b = 5; })(); console.log(b); 什么会被打印在控制台上?  回答:         上面的代码会打印 5。 &nbs</div> </li> <li><a href="/article/1573.htm" title="【Thrift二】Thrift Hello World" target="_blank">【Thrift二】Thrift Hello World</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/Hello+world/1.htm">Hello world</a> <div>本篇,不考虑细节问题和为什么,先照葫芦画瓢写一个Thrift版本的Hello World,了解Thrift RPC服务开发的基本流程   1. 在Intellij中创建一个Maven模块,加入对Thrift的依赖,同时还要加上slf4j依赖,如果不加slf4j依赖,在后面启动Thrift Server时会报错 <dependency> </div> </li> <li><a href="/article/1700.htm" title="【Avro一】Avro入门" target="_blank">【Avro一】Avro入门</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/%E5%85%A5%E9%97%A8/1.htm">入门</a> <div>本文的目的主要是总结下基于Avro Schema代码生成,然后进行序列化和反序列化开发的基本流程。需要指出的是,Avro并不要求一定得根据Schema文件生成代码,这对于动态类型语言很有用。   1. 添加Maven依赖   <?xml version="1.0" encoding="UTF-8"?> <proj</div> </li> <li><a href="/article/1827.htm" title="安装nginx+ngx_lua支持WAF防护功能" target="_blank">安装nginx+ngx_lua支持WAF防护功能</a> <span class="text-muted">ronin47</span> <div>需要的软件:LuaJIT-2.0.0.tar.gz                   nginx-1.4.4.tar.gz          &nb</div> </li> <li><a href="/article/1954.htm" title="java-5.查找最小的K个元素-使用最大堆" target="_blank">java-5.查找最小的K个元素-使用最大堆</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> import java.util.Arrays; import java.util.Random; public class MinKElement { /** * 5.最小的K个元素 * I would like to use MaxHeap. * using QuickSort is also OK */ public static void</div> </li> <li><a href="/article/2081.htm" title="TCP的TIME-WAIT" target="_blank">TCP的TIME-WAIT</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/socket/1.htm">socket</a> <div>原文连接: http://vincent.bernat.im/en/blog/2014-tcp-time-wait-state-linux.html 以下为对原文的阅读笔记 说明: 主动关闭的一方称为local end,被动关闭的一方称为remote end 本地IP、本地端口、远端IP、远端端口这一“四元组”称为quadruplet,也称为socket 1、TIME_WA</div> </li> <li><a href="/article/2208.htm" title="jquery ajax 序列化表单" target="_blank">jquery ajax 序列化表单</a> <span class="text-muted">coder_xpf</span> <a class="tag" taget="_blank" href="/search/Jquery+ajax+%E5%BA%8F%E5%88%97%E5%8C%96/1.htm">Jquery ajax 序列化</a> <div>   checkbox 如果不设定值,默认选中值为on;设定值之后,选中则为设定的值   <input type="checkbox" name="favor" id="favor" checked="checked"/> $("#favor&quo</div> </li> <li><a href="/article/2335.htm" title="Apache集群乱码和最高并发控制" target="_blank">Apache集群乱码和最高并发控制</a> <span class="text-muted">cuisuqiang</span> <a class="tag" taget="_blank" href="/search/apache/1.htm">apache</a><a class="tag" taget="_blank" href="/search/tomcat/1.htm">tomcat</a><a class="tag" taget="_blank" href="/search/%E5%B9%B6%E5%8F%91/1.htm">并发</a><a class="tag" taget="_blank" href="/search/%E9%9B%86%E7%BE%A4/1.htm">集群</a><a class="tag" taget="_blank" href="/search/%E4%B9%B1%E7%A0%81/1.htm">乱码</a> <div>都知道如果使用Http访问,那么在Connector中增加URIEncoding即可,其实使用AJP时也一样,增加useBodyEncodingForURI和URIEncoding即可。 最大连接数也是一样的,增加maxThreads属性即可,如下,配置如下: <Connector maxThreads="300" port="8019" prot</div> </li> <li><a href="/article/2462.htm" title="websocket" target="_blank">websocket</a> <span class="text-muted">dalan_123</span> <a class="tag" taget="_blank" href="/search/websocket/1.htm">websocket</a> <div>一、低延迟的客户端-服务器 和 服务器-客户端的连接 很多时候所谓的http的请求、响应的模式,都是客户端加载一个网页,直到用户在进行下一次点击的时候,什么都不会发生。并且所有的http的通信都是客户端控制的,这时候就需要用户的互动或定期轮训的,以便从服务器端加载新的数据。   通常采用的技术比如推送和comet(使用http长连接、无需安装浏览器安装插件的两种方式:基于ajax的长</div> </li> <li><a href="/article/2589.htm" title="菜鸟分析网络执法官" target="_blank">菜鸟分析网络执法官</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a> <div>  最近在论坛上看到很多贴子在讨论网络执法官的问题。菜鸟我正好知道这回事情.人道"人之患好为人师" 手里忍不住,就写点东西吧. 我也很忙.又没有MM,又没有MONEY....晕倒有点跑题. OK,闲话少说,切如正题. 要了解网络执法官的原理. 就要先了解局域网的通信的原理. 前面我们看到了.在以太网上传输的都是具有以太网头的数据包. </div> </li> <li><a href="/article/2716.htm" title="Android相对布局属性全集" target="_blank">Android相对布局属性全集</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a> <div>RelativeLayout布局android:layout_marginTop="25dip" //顶部距离android:gravity="left" //空间布局位置android:layout_marginLeft="15dip //距离左边距 // 相对于给定ID控件android:layout_above 将该控件的底部置于给定ID的</div> </li> <li><a href="/article/2843.htm" title="Tomcat内存设置详解" target="_blank">Tomcat内存设置详解</a> <span class="text-muted">eksliang</span> <a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/tomcat/1.htm">tomcat</a><a class="tag" taget="_blank" href="/search/tomcat%E5%86%85%E5%AD%98%E8%AE%BE%E7%BD%AE/1.htm">tomcat内存设置</a> <div>Java内存溢出详解   一、常见的Java内存溢出有以下三种:   1. java.lang.OutOfMemoryError: Java heap space ----JVM Heap(堆)溢出JVM在启动的时候会自动设置JVM Heap的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。 可以利用JVM提</div> </li> <li><a href="/article/2970.htm" title="Java6 JVM参数选项" target="_blank">Java6 JVM参数选项</a> <span class="text-muted">greatwqs</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/HotSpot/1.htm">HotSpot</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/jvm%E5%8F%82%E6%95%B0/1.htm">jvm参数</a><a class="tag" taget="_blank" href="/search/JVM+Options/1.htm">JVM Options</a> <div>Java 6 JVM参数选项大全(中文版)   作者:Ken Wu Email: ken.wug@gmail.com 转载本文档请注明原文链接 http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm!   本文是基于最新的SUN官方文档Java SE 6 Hotspot VM Opt</div> </li> <li><a href="/article/3097.htm" title="weblogic创建JMC" target="_blank">weblogic创建JMC</a> <span class="text-muted">i5land</span> <a class="tag" taget="_blank" href="/search/weblogic/1.htm">weblogic</a><a class="tag" taget="_blank" href="/search/jms/1.htm">jms</a> <div>进入 weblogic控制太 1.创建持久化存储 --Services--Persistant Stores--new--Create FileStores--name随便起--target默认--Directory写入在本机建立的文件夹的路径--ok 2.创建JMS服务器 --Services--Messaging--JMS Servers--new--name随便起--Pers</div> </li> <li><a href="/article/3224.htm" title="基于 DHT 网络的磁力链接和BT种子的搜索引擎架构" target="_blank">基于 DHT 网络的磁力链接和BT种子的搜索引擎架构</a> <span class="text-muted">justjavac</span> <a class="tag" taget="_blank" href="/search/DHT/1.htm">DHT</a> <div>上周开发了一个磁力链接和 BT 种子的搜索引擎 {Magnet & Torrent},本文简单介绍一下主要的系统功能和用到的技术。 系统包括几个独立的部分: 使用 Python 的 Scrapy 框架开发的网络爬虫,用来爬取磁力链接和种子; 使用 PHP CI 框架开发的简易网站; 搜索引擎目前直接使用的 MySQL,将来可以考虑使</div> </li> <li><a href="/article/3351.htm" title="sql添加、删除表中的列" target="_blank">sql添加、删除表中的列</a> <span class="text-muted">macroli</span> <a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a> <div>添加没有默认值:alter table Test add BazaarType char(1) 有默认值的添加列:alter table Test add BazaarType char(1) default(0) 删除没有默认值的列:alter table Test drop COLUMN BazaarType 删除有默认值的列:先删除约束(默认值)alter table Test DRO</div> </li> <li><a href="/article/3478.htm" title="PHP中二维数组的排序方法" target="_blank">PHP中二维数组的排序方法</a> <span class="text-muted">abc123456789cba</span> <a class="tag" taget="_blank" href="/search/%E6%8E%92%E5%BA%8F/1.htm">排序</a><a class="tag" taget="_blank" href="/search/%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84/1.htm">二维数组</a><a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a> <div><?php/*** @package     BugFree* @version     $Id: FunctionsMain.inc.php,v 1.32 2005/09/24 11:38:37 wwccss Exp $*** Sort an two-dimension array by some level </div> </li> <li><a href="/article/3605.htm" title="hive优化之------控制hive任务中的map数和reduce数" target="_blank">hive优化之------控制hive任务中的map数和reduce数</a> <span class="text-muted">superlxw1234</span> <a class="tag" taget="_blank" href="/search/hive/1.htm">hive</a><a class="tag" taget="_blank" href="/search/hive%E4%BC%98%E5%8C%96/1.htm">hive优化</a> <div>一、    控制hive任务中的map数: 1.    通常情况下,作业会通过input的目录产生一个或者多个map任务。 主要的决定因素有: input的文件总个数,input的文件大小,集群设置的文件块大小(目前为128M, 可在hive中通过set dfs.block.size;命令查看到,该参数不能自定义修改);2. </div> </li> <li><a href="/article/3732.htm" title="Spring Boot 1.2.4 发布" target="_blank">Spring Boot 1.2.4 发布</a> <span class="text-muted">wiselyman</span> <a class="tag" taget="_blank" href="/search/spring+boot/1.htm">spring boot</a> <div>Spring Boot 1.2.4已于6.4日发布,repo.spring.io and Maven Central可以下载(推荐使用maven或者gradle构建下载)。   这是一个维护版本,包含了一些修复small number of fixes,建议所有的用户升级。   Spring Boot 1.3的第一个里程碑版本将在几天后发布,包含许多</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>