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/1950230678696161280.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/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div>计数排序:是一种基于哈希的排序算法。他的基本思想是通过统计每个元素的出现次数,然后根据统计结果将元素依次放入排序后的序列中。这种排序算法适用于范围较小的情况,例如整数范围在0到k之间计数排序步骤:1初始化一个长度为最大元素值加1的计数数组,所有元素初始化为02遍历原始数组,将每个元素值作为索引,在计数数组中对应位置加13将数组清空4遍历计数器数组,按照数组中的元素个数放回到元数组中计数排序的优点和</div> </li> <li><a href="/article/1950227023859347456.htm" title="Linux系统配置(应用程序)" target="_blank">Linux系统配置(应用程序)</a> <span class="text-muted">1风天云月</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/%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F/1.htm">应用程序</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85/1.htm">编译安装</a><a class="tag" taget="_blank" href="/search/rpm/1.htm">rpm</a><a class="tag" taget="_blank" href="/search/http/1.htm">http</a> <div>目录前言一、应用程序概述1、命令与程序的关系2、程序的组成3、软件包封装类型二、RPM1、RPM概述2、RPM用法三、编译安装1、解包2、配置3、编译4、安装5、启用httpd服务结语前言在Linux中的应用程序被视为将软件包安装到系统中后产生的各种文档,其中包括可执行文件、配置文件、用户手册等内容,这些文档被组织为一个有机的整体,为用户提供特定的功能,因此对于“安装软件包”与“安装应用程序”这两</div> </li> <li><a href="/article/1950226770049429504.htm" title="【C++算法】76.优先级队列_前 K 个高频单词" target="_blank">【C++算法】76.优先级队列_前 K 个高频单词</a> <span class="text-muted">流星白龙</span> <a class="tag" taget="_blank" href="/search/%E4%BC%98%E9%80%89%E7%AE%97%E6%B3%95C%2B%2B/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/%E7%AE%97%E6%B3%95/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> <div>文章目录题目链接:题目描述:解法C++算法代码:题目链接:692.前K个高频单词题目描述:解法利用堆来解决TopK问题预处理一下原始的字符串数组,用一个哈希表统计一下每一个单词出现的频次。创建一个大小为k的堆频次:小根堆字典序(频次相同的时候):大根堆循环让元素依次进堆判断提取结果C++算法代码:classSolution{//定义类型别名,PSI表示对typedefpairPSI;//自定义比较</div> </li> <li><a href="/article/1950219166367674368.htm" title="包含日志获取webshell" target="_blank">包含日志获取webshell</a> <span class="text-muted">陈望_ning</span> <div>日志文件关闭:Apache目录下的httpd.conf文件#ErrorLog"logs/error.log"#CustomLog"logs/access.log"common加#号为注释不产生日志文件如果去掉#将会在Apache/logs/目录下产生日志文件linux:access_logerror_logwindows:access.logerror.logaccess_log每一行记录了一次网</div> </li> <li><a href="/article/1950216800016855040.htm" title="Effective C++ 条款10:令operator=返回一个reference to *this" target="_blank">Effective C++ 条款10:令operator=返回一个reference to *this</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/c%2B%2B/1.htm">c++</a> <div>EffectiveC++条款10:令operator=返回一个referenceto*this核心思想:赋值操作符(operator=)应始终返回当前对象的引用(*this),以实现连锁赋值并保持与内置类型一致的语义。⚠️1.问题场景:违反连锁赋值语义classWidget{public:voidoperator=(constWidget&rhs){//错误:返回voidvalue=rhs.val</div> </li> <li><a href="/article/1950214784112717824.htm" title="C++ :vector的模拟" target="_blank">C++ :vector的模拟</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>目录一、vector的迭代器二、vector的构造函数默认构造函数参数构造函数迭代器范围构造函数拷贝构造函数swap:交换vector重载赋值符析构函数reserve:扩容vectorresize:调整大小push_back:添加元素empty:判空pop_back:后删获取大小与容量:size(),capacity()重载operator[]:元素访问insert:插入元素erase:删除一个元</div> </li> <li><a href="/article/1950209621381672960.htm" title="Android 应用权限管理详解" target="_blank">Android 应用权限管理详解</a> <span class="text-muted"></span> <div>文章目录1.权限类型2.权限请求机制3.权限组和分级4.权限管理的演进5.权限监控和SELinux强制访问控制6.应用权限审核和GooglePlayProtect7.开发者最佳实践8.用户权限管理9.Android应用沙箱模型10.ScopedStorage(分区存储)11.背景位置权限(BackgroundLocationAccess)12.权限回收和自动清理13.权限请求的用户体验设计14.G</div> </li> <li><a href="/article/1950206970766094336.htm" title="C++编程基础与面向对象概念解析" target="_blank">C++编程基础与面向对象概念解析</a> <span class="text-muted">侯昂</span> <a class="tag" taget="_blank" href="/search/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B/1.htm">面向对象编程</a><a class="tag" taget="_blank" href="/search/C%2B%2B%E8%AF%AD%E6%B3%95/1.htm">C++语法</a><a class="tag" taget="_blank" href="/search/%E5%87%BD%E6%95%B0/1.htm">函数</a><a class="tag" taget="_blank" href="/search/%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/1.htm">类与对象</a><a class="tag" taget="_blank" href="/search/%E7%BB%A7%E6%89%BF%E4%B8%8E%E5%A4%9A%E6%80%81%E6%80%A7/1.htm">继承与多态性</a> <div>C++编程基础与面向对象概念解析背景简介C++是一种广泛使用的面向对象编程语言,它允许开发者创建高效、灵活且功能强大的程序。本文基于《C++Primer》一书的章节内容,深入解析C++的核心概念和面向对象编程原则,旨在帮助读者构建扎实的C++编程基础。面向对象编程的原则软件危机与进化介绍了软件危机的产生和软件进化的必要性,强调了面向对象编程(OOP)在应对这些问题中的优势。面向对象编程范式讨论了面</div> </li> <li><a href="/article/1950202054706262016.htm" title="centos7安装配置 Anaconda3" target="_blank">centos7安装配置 Anaconda3</a> <span class="text-muted"></span> <div>Anaconda是一个用于科学计算的Python发行版,Anaconda于Python,相当于centos于linux。下载[root@testsrc]#mwgethttps://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-5.2.0-Linux-x86_64.shBegintodownload:Anaconda3-5.2.0-L</div> </li> <li><a href="/article/1950200667587014656.htm" title="学C++的五大惊人好处" target="_blank">学C++的五大惊人好处</a> <span class="text-muted"></span> <div>为什么要学c++学c++有什么用学习c++的好处有1.中考可以加分2.高考可能直接录取3.就业广且工资高4.在未来30--50年c++一定是一个很受欢迎的职业5.c++成功的例子deepsick等AI智能C++语言兼备编程效率和编译运行效率的语言C++语言是C语言功能增强版,在c语言的基础上添加了面向对象编程和泛型编程的支持既继承了C语言高效,简洁,快速和可移植的传统,又具备类似Java、Go等其</div> </li> <li><a href="/article/1950194742100815872.htm" title="用代码生成艺术字:设计个性化海报的秘密" target="_blank">用代码生成艺术字:设计个性化海报的秘密</a> <span class="text-muted"></span> <div>本文围绕“用代码生成艺术字:设计个性化海报的秘密”展开,先概述代码生成艺术字在海报设计中的独特价值,接着介绍常用的代码工具(如HTML、CSS、JavaScript等),详细阐述从构思到实现的完整流程,包括字体样式设计、动态效果添加等,还分享了提升艺术字质感的技巧及实际案例。最后总结代码生成艺术字的优势,为设计师提供打造个性化海报的实用指南,助力提升海报设计的独特性与吸引力,符合搜索引擎SEO标准</div> </li> <li><a href="/article/1950181127176122368.htm" title="C++中std::variant的使用详解和实战代码示例" target="_blank">C++中std::variant的使用详解和实战代码示例</a> <span class="text-muted">点云SLAM</span> <a class="tag" taget="_blank" href="/search/C%2B%2B/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/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/variant/1.htm">variant</a><a class="tag" taget="_blank" href="/search/C%2B%2B%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/1.htm">C++泛型编程</a><a class="tag" taget="_blank" href="/search/%E8%81%94%E5%90%88%E4%BD%93/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%B1%BB%E5%9E%8B%E6%93%A6%E9%99%A4%E6%9C%BA%E5%88%B6/1.htm">类型擦除机制</a><a class="tag" taget="_blank" href="/search/C%2B%2B17/1.htm">C++17</a> <div>std::variant是C++17引入的一个类型安全的联合体(type-safeunion),它可以在多个类型之间存储一个值,并在编译时进行类型检查。它是现代C++类型擦除与泛型编程的核心工具之一,适用于构建可变类型结构、消息传递系统、状态机等。一、基本概念#includestd::variantv;类似于联合体union,但类型安全。std::variant只能存储其中一个类型的值。默认构造时</div> </li> <li><a href="/article/1950178809030438912.htm" title="今年校招竞争真激烈" target="_blank">今年校招竞争真激烈</a> <span class="text-muted">12_05</span> <div>程序员满大街,都要找不到工作了。即使人工智能满大街,我也后悔当初没学机器学习,后悔当初没学Java。C++真难找工作。难道毕了业就失业吗?好担心!</div> </li> <li><a href="/article/1950175199089455104.htm" title="PDF转Markdown - Python 实现方案与代码" target="_blank">PDF转Markdown - Python 实现方案与代码</a> <span class="text-muted">Eiceblue</span> <a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/PDF/1.htm">PDF</a><a class="tag" taget="_blank" href="/search/pdf/1.htm">pdf</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</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/vscode/1.htm">vscode</a> <div>PDF作为广泛使用的文档格式,转换为轻量级标记语言Markdown后,可无缝集成到技术文档、博客平台和版本控制系统中,提高内容的可编辑性和可访问性。本文将详细介绍如何使用国产Spire.PDFforPython库将PDF文档转换为Markdown格式。技术优势:精准保留原始文档结构(段落/列表/表格)完整提取文本和图像内容无需Adobe依赖的纯Python实现支持Linux/Windows/mac</div> </li> <li><a href="/article/1950175200347746304.htm" title="深入剖析 boost::unique_lock<boost::mutex>" target="_blank">深入剖析 boost::unique_lock<boost::mutex></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/Boost%E5%BA%93/1.htm">Boost库</a><a class="tag" taget="_blank" href="/search/C%2FC%2B%2B%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E4%B8%93%E9%A2%98/1.htm">C/C++多线程编程专题</a><a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a><a class="tag" taget="_blank" href="/search/boost/1.htm">boost</a><a class="tag" taget="_blank" href="/search/%E7%BA%BF%E7%A8%8B/1.htm">线程</a><a class="tag" taget="_blank" href="/search/%E9%94%81/1.htm">锁</a> <div>在高并发的C++程序中,线程安全是永恒的主题。而boost::unique_lock作为Boost.Thread库中的核心组件,为开发者提供了强大、灵活且异常安全的互斥量管理机制。它不仅是RAII(ResourceAcquisitionIsInitialization)设计模式的典范,更是实现复杂线程同步逻辑的基石。一、从lock_guard的说起在介绍unique_lock之前,我们先回顾其“简</div> </li> <li><a href="/article/1950170537426743296.htm" title="在Windows11上安装Linux操作系统的几种技术方案" target="_blank">在Windows11上安装Linux操作系统的几种技术方案</a> <span class="text-muted">yuanpan</span> <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>在Windows11上安装Linux主要有以下几种技术方案,每种方案适用于不同的需求场景:1.WindowsSubsystemforLinux(WSL)适用场景:开发、命令行工具、轻量级Linux环境支持发行版:Ubuntu、Debian、KaliLinux、Fedora等优点:轻量级:无需虚拟机,直接在Windows上运行Linux命令行环境。无缝集成:可访问Windows文件系统,支持VSCo</div> </li> <li><a href="/article/1950169523193704448.htm" title="Claude Code 超详细完整指南(2025最新版)" target="_blank">Claude Code 超详细完整指南(2025最新版)</a> <span class="text-muted">笙囧同学</span> <a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>终端AI编程助手|高频使用点+生态工具+完整命令参考+最新MCP配置目录快速开始(5分钟上手)详细安装指南系统要求Windows安装(WSL方案)macOS安装Linux安装安装验证配置与认证首次认证环境变量配置代理配置⚡基础命令详解启动命令会话管理文件操作Think模式完全指南MCP服务器配置详解MCP基础概念添加MCP服务器10个必备MCP服务器MCP故障排除记忆系统详解高级使用技巧成本控制策</div> </li> <li><a href="/article/1950166876634017792.htm" title="如何在 Ubuntu 24.04 或 22.04 Linux 上安装和运行 Redis 服务器" target="_blank">如何在 Ubuntu 24.04 或 22.04 Linux 上安装和运行 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/%E8%BF%90%E7%BB%B4%E5%8F%8A%E4%BD%BF%E7%94%A8/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><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a> <div>Redis(RemoteDictionaryServer,远程字典服务器)是一种内存数据结构存储,通常用作NoSQL数据库、缓存和消息代理。它是开源的,因此用户可以免费安装,无需支付任何费用。Redis旨在为需要快速数据访问和低延迟的应用程序提供速度和效率。Redis支持多种数据类型,包括字符串(Strings)、列表(Lists)、集合(Sets)、哈希(Hashes)、有序集合(SortedS</div> </li> <li><a href="/article/1950163093577330688.htm" title="Linux中Samba服务器安装与配置文件" target="_blank">Linux中Samba服务器安装与配置文件</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/%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>Samba简述27zkqsamba是一个基于TCP/IP协议的开源软件套件,可以在Linux、Windows、macOS等操作系统上运行。它允许不同操作系统的计算机之间实现文件和打印机共享。samba提供了一个服务,使得Windows操作系统可以像访问本地文件一样访问Linux、Mac等操作系统上的共享文件。实现跨平台的文件共享,提高办公环境的效率和便利性。samba也支持Windows网络邻居协</div> </li> <li><a href="/article/1950152629841620992.htm" title="ubuntu qt环境下出现No suitable kits found解决方案" target="_blank">ubuntu qt环境下出现No suitable kits found解决方案</a> <span class="text-muted"></span> <div>1.清理QtCreator缓存QtCreator会缓存项目配置、索引等数据,可能导致某些异常。清理方法:(1)删除QtCreator配置目录bashrm-rf~/.config/QtProject/(Ubuntu/Linux)或Windows:cmdrmdir/s/q"%APPDATA%\QtProject"(2)清除QtCreator的编译缓存bashrm-rf~/.cache/QtProjec</div> </li> <li><a href="/article/1950152503563710464.htm" title="如何在 Ubuntu 24.04 或 22.04 Linux 上安装和使用 NoMachine" target="_blank">如何在 Ubuntu 24.04 或 22.04 Linux 上安装和使用 NoMachine</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/%E8%BF%90%E7%BB%B4%E5%8F%8A%E4%BD%BF%E7%94%A8/1.htm">运维及使用</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/nomachine/1.htm">nomachine</a><a class="tag" taget="_blank" href="/search/%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5/1.htm">远程连接</a> <div>NoMachine是一款适用于Linux(Ubuntu)及其他支持的操作系统的远程桌面应用程序,允许用户通过本地或远程系统从世界任何地方控制计算机。它可以在低带宽连接下工作,被专业人士和家庭用户广泛使用。NoMachine的主要功能高性能远程访问跨平台兼容性易于使用,因为用户界面友好提供强大的加密协议,如SSH、SSL及其他安全标准支持远程文件传输和打印服务允许从远程计算机进行音频和视频流媒体传输</div> </li> <li><a href="/article/1950146516492546048.htm" title="搜索引擎技术选型" target="_blank">搜索引擎技术选型</a> <span class="text-muted">dusty_giser</span> <div>  近期,业主对POI检索提出了一些想法,针对之前简单的WordSegment分词和模糊匹配搜索需要进行一些更为符合业主需求的调整。于是这几天对搜索引擎进行了一些技术选型;一、ApacheLucene  Lucene是一个开源的高性能、可扩展的全文检索引擎工具包,但不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎。所以它是一套信息检索工具包,可以说是当今最先进</div> </li> <li><a href="/article/1950141538352820224.htm" title="2025.07 Java入门笔记01" target="_blank">2025.07 Java入门笔记01</a> <span class="text-muted">殷浩焕</span> <a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a> <div>一、熟悉IDEA和Java语法(一)LiuCourseJavaOOP1.一直在用C++开发,python也用了些,Java是真的不熟,用什么IDE还是问的同事;2.一开始安装了jdk-23,拿VSCode当编辑器,在cmd窗口编译运行,也能玩;但是想正儿八经搞项目开发,还是需要IDE;3.安装了IDEA社区版:(1)IDE通常自带对应编程语言的安装包,例如IDEA自带jbr-21(和jdk是不同的</div> </li> <li><a href="/article/1950135356531732480.htm" title="linux实战--日志管理" target="_blank">linux实战--日志管理</a> <span class="text-muted"></span> <div>简介日志文件重要的信息系统文件,及了许多重要的系统事件,包括用户的登录信息,系统的启动信息,系统的安全信息,邮寄相关信息,各种服务相关的信息。日志对安全也很重要。每天记录系统发生的各种事情,通过日志检查错误发生的原因或受到攻击时攻击者留下的痕迹。总的来说,日志是记录重大事件的文件。处理日志的工具rsyslog系统日志管理专职管理日志的工具,它产生各种信息文件,主要存放在/var/loglogrot</div> </li> <li><a href="/article/1950134096558616576.htm" title="Python STL概念学习与代码实践" target="_blank">Python STL概念学习与代码实践</a> <span class="text-muted">体制教科书</span> <div>本文还有配套的精品资源,点击获取简介:通过”py_stl_learning”项目,学习者可以使用Python实现和理解C++STL的概念,包括数据结构、算法、容器适配器、模板和泛型容器等。Python中的列表、集合、字典等数据结构与STL中的vector、set、map等类似,而Python的itertools和functools模块提供了STL风格的算法功能。Python通过其面向对象的特性以及</div> </li> <li><a href="/article/1950130692448907264.htm" title="Vue CSR 到 Nuxt 3 SSR 迁移:技术实现与问题解决实录" target="_blank">Vue CSR 到 Nuxt 3 SSR 迁移:技术实现与问题解决实录</a> <span class="text-muted">二倍速播放</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a> <div>1.迁移动机与技术选型1.1CSR架构的局限性基于Vue3和Vite构建的客户端渲染(CSR)单页应用(SPA)提供了良好的开发体验和用户交互流畅性。但是其核心局限在于:搜索引擎优化(SEO):初始HTML响应仅包含一个根div元素,实际内容由JavaScript在浏览器端动态生成。虽然主流搜索引擎(如Google)能够执行部分JavaScript,但其抓取效率和稳定性不如直接获取完整HTML。非</div> </li> <li><a href="/article/1950114557917720576.htm" title="Windows系统第一次运行C语言程序,环境配置,软件安装等遇到的坑及解决方法" target="_blank">Windows系统第一次运行C语言程序,环境配置,软件安装等遇到的坑及解决方法</a> <span class="text-muted">灬爱码士灬</span> <a class="tag" taget="_blank" href="/search/windows/1.htm">windows</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/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>明确需要编辑器和编译器,并选择自己要用什么(我选的编辑器是VSCode:VisualStudioCode;编译器是gcc)下载VSCode并配置环境变量(这里没啥问题),安装C/C++的拓展安装Cygwin,用来在Windows操作系统上模拟Unix/Linux环境(Cygwin官网:https://www.cygwin.com/。)安装过程中镜像可以选择https://mirrors.aliyu</div> </li> <li><a href="/article/1950114054009843712.htm" title="三种接口实现增删改查" target="_blank">三种接口实现增删改查</a> <span class="text-muted">灬爱码士灬</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>目录ArrayListHashSetHashMapArrayListArrayList实现增删改查packagetest;importjava.util.ArrayList;importjava.util.Iterator;/***@Author:Mo*@Date:2020/12/611:11*/publicclasslist{ArrayListlist1=newArrayList();publi</div> </li> <li><a href="/article/1950110777142734848.htm" title="一篇教你学会Git" target="_blank">一篇教你学会Git</a> <span class="text-muted">编程界的彭于晏qaq</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/GIT/1.htm">GIT</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a> <div>从安装到高级使用(2025最新版)引言:为什么Git是开发者必备技能Git(GlobalInformationTracker)作为最流行的分布式版本控制系统,由Linux之父LinusTorvalds于2005年创建,现已成为软件开发的基础设施。与传统集中式版本控制系统(如SVN)相比,Git具有三大核心优势:分布式架构:每个开发者本地都有完整仓库副本,支持离线工作高效分支管理:创建和切换分支几乎</div> </li> <li><a href="/article/1950102202521546752.htm" title="基本服务 FTP & SMB" target="_blank">基本服务 FTP & SMB</a> <span class="text-muted">会飞的灰大狼</span> <a class="tag" taget="_blank" href="/search/Centos7/1.htm">Centos7</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>基本服务FTP&SMB前言:FTP简称为文件传输协议前面说的他可以做到备份的功能那么它可以做到文件传输的过程smb我们简单来说共享文件夹‍NFSNFS(NetworkFileSystem,网络文件系统)是一种分布式文件系统协议,允许不同计算机之间通过网络共享文件和目录,使远程文件系统像本地文件系统一样被访问。它最初由SunMicrosystems开发,现在已成为UNIX/Linux系统中常用的网络</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>