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/1835513699826233344.htm" title="android系统selinux中添加新属性property" target="_blank">android系统selinux中添加新属性property</a> <span class="text-muted">辉色投像</span> <div>1.定位/android/system/sepolicy/private/property_contexts声明属性开头:persist.charge声明属性类型:u:object_r:system_prop:s0图12.定位到android/system/sepolicy/public/domain.te删除neverallow{domain-init}default_prop:property</div> </li> <li><a href="/article/1835511030260789248.htm" title="c++ 的iostream 和 c++的stdio的区别和联系" target="_blank">c++ 的iostream 和 c++的stdio的区别和联系</a> <span class="text-muted">黄卷青灯77</span> <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><a class="tag" taget="_blank" href="/search/iostream/1.htm">iostream</a><a class="tag" taget="_blank" href="/search/stdio/1.htm">stdio</a> <div>在C++中,iostream和C语言的stdio.h都是用于处理输入输出的库,但它们在设计、用法和功能上有许多不同。以下是两者的区别和联系:区别1.编程风格iostream(C++风格):C++标准库中的输入输出流类库,支持面向对象的输入输出操作。典型用法是cin(输入)和cout(输出),使用>操作符来处理数据。更加类型安全,支持用户自定义类型的输入输出。#includeintmain(){in</div> </li> <li><a href="/article/1835509391361667072.htm" title="Linux下QT开发的动态库界面弹出操作(SDL2)" target="_blank">Linux下QT开发的动态库界面弹出操作(SDL2)</a> <span class="text-muted">13jjyao</span> <a class="tag" taget="_blank" href="/search/QT%E7%B1%BB/1.htm">QT类</a><a class="tag" taget="_blank" href="/search/qt/1.htm">qt</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/sdl2/1.htm">sdl2</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>需求:操作系统为linux,开发框架为qt,做成需带界面的qt动态库,调用方为java等非qt程序难点:调用方为java等非qt程序,也就是说调用方肯定不带QApplication::exec(),缺少了这个,QTimer等事件和QT创建的窗口将不能弹出(包括opencv也是不能弹出);这与qt调用本身qt库是有本质的区别的思路:1.调用方缺QApplication::exec(),那么我们在接口</div> </li> <li><a href="/article/1835504596898902016.htm" title="linux sdl windows.h,Windows下的SDL安装" target="_blank">linux sdl windows.h,Windows下的SDL安装</a> <span class="text-muted">奔跑吧linux内核</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/sdl/1.htm">sdl</a><a class="tag" taget="_blank" href="/search/windows.h/1.htm">windows.h</a> <div>首先你要下载并安装SDL开发包。如果装在C盘下,路径为C:\SDL1.2.5如果在WINDOWS下。你可以按以下步骤:1.打开VC++,点击"Tools",Options2,点击directories选项3.选择"Includefiles"增加一个新的路径。"C:\SDL1.2.5\include"4,现在选择"Libaryfiles“增加"C:\SDL1.2.5\lib"现在你可以开始编写你的第</div> </li> <li><a href="/article/1835503712899002368.htm" title="linux中sdl的使用教程,sdl使用入门" target="_blank">linux中sdl的使用教程,sdl使用入门</a> <span class="text-muted">Melissa Corvinus</span> <a class="tag" taget="_blank" href="/search/linux%E4%B8%ADsdl%E7%9A%84%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/1.htm">linux中sdl的使用教程</a> <div>本文通过一个简单示例讲解SDL的基本使用流程。示例中展示一个窗口,窗口里面有个随机颜色快随机移动。当我们鼠标点击关闭按钮时间窗口关闭。基本步骤如下:1.初始化SDL并创建一个窗口。SDL_Init()初始化SDL_CreateWindow()创建窗口2.纹理渲染存储RGB和存储纹理的区别:比如一个从左到右由红色渐变到蓝色的矩形,用存储RGB的话就需要把矩形中每个点的具体颜色值存储下来;而纹理只是一</div> </li> <li><a href="/article/1835502578050363392.htm" title="PHP环境搭建详细教程" target="_blank">PHP环境搭建详细教程</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/php/1.htm">php</a> <div>PHP是一个流行的服务器端脚本语言,广泛用于Web开发。为了使PHP能够在本地或服务器上运行,我们需要搭建一个合适的PHP环境。本教程将结合最新资料,介绍在不同操作系统上搭建PHP开发环境的多种方法,包括Windows、macOS和Linux系统的安装步骤,以及本地和Docker环境的配置。1.PHP环境搭建概述PHP环境的搭建主要分为以下几类:集成开发环境:例如XAMPP、WAMP、MAMP,这</div> </li> <li><a href="/article/1835501948011376640.htm" title="使用 FinalShell 进行远程连接(ssh 远程连接 Linux 服务器)" target="_blank">使用 FinalShell 进行远程连接(ssh 远程连接 Linux 服务器)</a> <span class="text-muted">编程经验分享</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/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/ssh/1.htm">ssh</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>目录前言基本使用教程新建远程连接连接主机自定义命令路由追踪前言后端开发,必然需要和服务器打交道,部署应用,排查问题,查看运行日志等等。一般服务器都是集中部署在机房中,也有一些直接是云服务器,总而言之,程序员不可能直接和服务器直接操作,一般都是通过ssh连接来登录服务器。刚接触远程连接时,使用的是XSHELL来远程连接服务器,连接上就能够操作远程服务器了,但是仅用XSHELL并没有上传下载文件的功能</div> </li> <li><a href="/article/1835494258262503424.htm" title="【JS】执行时长(100分) |思路参考+代码解析(C++)" target="_blank">【JS】执行时长(100分) |思路参考+代码解析(C++)</a> <span class="text-muted">l939035548</span> <a class="tag" taget="_blank" href="/search/JS/1.htm">JS</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/c%2B%2B/1.htm">c++</a> <div>题目为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。假设GPU最多一次执行n个任务,一次执行耗时1秒,在保证GPU不空闲情况下,最少需要多长时间执行完成。题目输入第一个参数为GPU一次最多执行的任务个数,取值范围[1,10000]第二个参数为任务数组长度,取值范围[1,10000]第三个参数为任务数组,数字范围</div> </li> <li><a href="/article/1835493373906087936.htm" title="libyuv之linux编译" target="_blank">libyuv之linux编译</a> <span class="text-muted">jaronho</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>文章目录一、下载源码二、编译源码三、注意事项1、银河麒麟系统(aarch64)(1)解决armv8-a+dotprod+i8mm指令集支持问题(2)解决armv9-a+sve2指令集支持问题一、下载源码到GitHub网站下载https://github.com/lemenkov/libyuv源码,或者用直接用git克隆到本地,如:gitclonehttps://github.com/lemenko</div> </li> <li><a href="/article/1835489588169240576.htm" title="ARM驱动学习之5 LEDS驱动" target="_blank">ARM驱动学习之5 LEDS驱动</a> <span class="text-muted">JT灬新一</span> <a class="tag" taget="_blank" href="/search/%E5%B5%8C%E5%85%A5%E5%BC%8F/1.htm">嵌入式</a><a class="tag" taget="_blank" href="/search/C/1.htm">C</a><a class="tag" taget="_blank" href="/search/%E5%BA%95%E5%B1%82/1.htm">底层</a><a class="tag" taget="_blank" href="/search/arm%E5%BC%80%E5%8F%91/1.htm">arm开发</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/1.htm">单片机</a> <div>ARM驱动学习之5LEDS驱动知识点:•linuxGPIO申请函数和赋值函数–gpio_request–gpio_set_value•三星平台配置GPIO函数–s3c_gpio_cfgpin•GPIO配置输出模式的宏变量–S3C_GPIO_OUTPUT注意点:DRIVER_NAME和DEVICE_NAME匹配。实现步骤:1.加入需要的头文件://Linux平台的gpio头文件#include//三</div> </li> <li><a href="/article/1835489207716507648.htm" title="基于CODESYS的多轴运动控制程序框架:逻辑与运动控制分离,快速开发灵活操作" target="_blank">基于CODESYS的多轴运动控制程序框架:逻辑与运动控制分离,快速开发灵活操作</a> <span class="text-muted">GPJnCrbBdl</span> <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> <div>基于codesys开发的多轴运动控制程序框架,将逻辑与运动控制分离,将单轴控制封装成功能块,对该功能块的操作包含了所有的单轴控制(归零、点动、相对定位、绝对定位、设置当前位置、伺服模式切换等等)。程序框架由主程序按照状态调用分归零模式、手动模式、自动模式、故障模式,程序状态的跳转都已完成,只需要根据不同的工艺要求完成所需的动作即可。变量的声明、地址的规划都严格按照C++的标准定义,能帮助开发者快速</div> </li> <li><a href="/article/1835489081480540160.htm" title="C++ | Leetcode C++题解之第409题最长回文串" target="_blank">C++ | Leetcode C++题解之第409题最长回文串</a> <span class="text-muted">Ddddddd_158</span> <a class="tag" taget="_blank" href="/search/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/1.htm">经验分享</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/%E9%A2%98%E8%A7%A3/1.htm">题解</a> <div>题目:题解:classSolution{public:intlongestPalindrome(strings){unordered_mapcount;intans=0;for(charc:s)++count[c];for(autop:count){intv=p.second;ans+=v/2*2;if(v%2==1andans%2==0)++ans;}returnans;}};</div> </li> <li><a href="/article/1835488955101966336.htm" title="C++菜鸟教程 - 从入门到精通 第二节" target="_blank">C++菜鸟教程 - 从入门到精通 第二节</a> <span class="text-muted">DreamByte</span> <a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a> <div>一.上节课的补充(数据类型)1.前言继上节课,我们主要讲解了输入,输出和运算符,我们现在来补充一下数据类型的知识上节课遗漏了这个知识点,非常的抱歉顺便说一下,博主要上高中了,更新会慢,2-4周更新一次对了,正好赶上中秋节,小编跟大家说一句:中秋节快乐!2.int类型上节课,我们其实只用了int类型int类型,是整数类型,它们存贮的是整数,不能存小数(浮点数)定义变量的方式很简单inta;//定义一</div> </li> <li><a href="/article/1835485681187647488.htm" title="【华为OD技术面试真题精选 - 非技术题】 -HR面,综合面_华为od hr面" target="_blank">【华为OD技术面试真题精选 - 非技术题】 -HR面,综合面_华为od hr面</a> <span class="text-muted">一个射手座的程序媛</span> <a class="tag" taget="_blank" href="/search/%E7%A8%8B%E5%BA%8F%E5%91%98/1.htm">程序员</a><a class="tag" taget="_blank" href="/search/%E5%8D%8E%E4%B8%BAod/1.htm">华为od</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95/1.htm">面试</a><a class="tag" taget="_blank" href="/search/%E8%81%8C%E5%9C%BA%E5%92%8C%E5%8F%91%E5%B1%95/1.htm">职场和发展</a> <div>最后的话最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!资料预览给大家整理的视频资料:给大家整理的电子书资料:如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化的资料的朋友,可以点击这里获</div> </li> <li><a href="/article/1835482277870661632.htm" title="简介Shell、zsh、bash" target="_blank">简介Shell、zsh、bash</a> <span class="text-muted">zhaosuningsn</span> <a class="tag" taget="_blank" href="/search/Shell/1.htm">Shell</a><a class="tag" taget="_blank" href="/search/zsh/1.htm">zsh</a><a class="tag" taget="_blank" href="/search/bash/1.htm">bash</a><a class="tag" taget="_blank" href="/search/shell/1.htm">shell</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/bash/1.htm">bash</a> <div>Shell是Linux和Unix的外壳,类似衣服,负责外界与Linux和Unix内核的交互联系。例如接收终端用户及各种应用程序的命令,把接收的命令翻译成内核能理解的语言,传递给内核,并把内核处理接收的命令的结果返回给外界,即Shell是外界和内核沟通的桥梁或大门。Linux和Unix提供了多种Shell,其中有种bash,当然还有其他好多种。Mac电脑中不但有bash,还有一个zsh,预装的,据说</div> </li> <li><a href="/article/1835479000600899584.htm" title="Linux MariaDB使用OpenSSL安装SSL证书" target="_blank">Linux MariaDB使用OpenSSL安装SSL证书</a> <span class="text-muted">Meta39</span> <a class="tag" taget="_blank" href="/search/MySQL/1.htm">MySQL</a><a class="tag" taget="_blank" href="/search/Oracle/1.htm">Oracle</a><a class="tag" taget="_blank" href="/search/MariaDB/1.htm">MariaDB</a><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/ssl/1.htm">ssl</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/mariadb/1.htm">mariadb</a> <div>进入到证书存放目录,批量删除.pem证书警告:确保已经进入到证书存放目录find.-typef-iname\*.pem-delete查看是否安装OpenSSLopensslversion没有则安装yuminstallopensslopenssl-devel开启SSL编辑/etc/my.cnf文件(没有的话就创建,但是要注意,在/etc/my.cnf.d/server.cnf配置了datadir的,</div> </li> <li><a href="/article/1835469294436184064.htm" title="【从浅识到熟知Linux】Linux发展史" target="_blank">【从浅识到熟知Linux】Linux发展史</a> <span class="text-muted">Jammingpro</span> <a class="tag" taget="_blank" href="/search/%E4%BB%8E%E6%B5%85%E5%AD%A6%E5%88%B0%E7%86%9F%E7%9F%A5Linux/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个人主页:Jammingpro每日努力一点点,技术变化看得见文章前言:本篇文章记录Linux发展的历史,因在介绍Linux过程中涉及的其他操作系统及人物,本文对相关内容也有所介绍。文章目录Unix发展史Linux发展史开源Linux官网企业应用情况发行版本在学习Linux前,我们可能都会问Linux从哪里来?它是如何发展的。但在介绍Linux之前,需要先介绍一下Un</div> </li> <li><a href="/article/1835467782687387648.htm" title="linux 发展史" target="_blank">linux 发展史</a> <span class="text-muted">种树的猴子</span> <a class="tag" taget="_blank" href="/search/%E5%86%85%E6%A0%B8/1.htm">内核</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/1.htm">操作系统</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a> <div>linux发展史说明此前对linux认识模糊一知半解,近期通过学习将自己对于linux的发展总结一下方便大家日后的学习。那Linux是目前一款非常火热的开源操作系统,可是linux是什么时候出现的,又是因为什么样的原因被开发出来的呢。以下将对linux的发展历程进行详细的讲解。目录一、Linux发展背景二、UINIX的诞生三、UNIX的重要分支-BSD的诞生四、Minix的诞生五、GNU与Free</div> </li> <li><a href="/article/1835466523163062272.htm" title="Linux sh命令" target="_blank">Linux sh命令</a> <span class="text-muted">fengyehongWorld</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> <div>目录一.基本语法二.选项2.1-c字符串中读取内容,并执行2.1.1基本用法2.1.2获取当前目录下失效的超链接2.2-x每个命令执行之前,将其打印出来2.3结合Here文档使用一.基本语法⏹Linux和Unix系统中用于执行shell脚本或运行命令的命令。sh[选项][脚本文件][参数...]⏹选项-c:从字符串中读取内容,并执行。-x:在每个命令执行之前,将其打印出来。-s:从标准流中读取内容</div> </li> <li><a href="/article/1835466270955368448.htm" title="Linux vi常用命令" target="_blank">Linux vi常用命令</a> <span class="text-muted">fengyehongWorld</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> <div>参考资料viコマンド(vimコマンド)リファレンス目录一.保存系命令二.删除系命令三.移动系命令四.复制粘贴系命令一.保存系命令⏹保存并退出:wq⏹强制保存并退出:wq!⏹退出(文件未编辑):q⏹强制退出(忽略已编辑内容):q!⏹另存为:w新文件名二.删除系命令⏹删除当前行dd⏹清空整个文档gg:移动到文档顶部dG:删除到最后一行ggdG三.移动系命令⏹移动到文档顶部gg⏹移动到文档底部#方式1G</div> </li> <li><a href="/article/1835464504918503424.htm" title="Java面试题精选:消息队列(二)" target="_blank">Java面试题精选:消息队列(二)</a> <span class="text-muted">芒果不是芒</span> <a class="tag" taget="_blank" href="/search/Java%E9%9D%A2%E8%AF%95%E9%A2%98%E7%B2%BE%E9%80%89/1.htm">Java面试题精选</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a> <div>一、Kafka的特性1.消息持久化:消息存储在磁盘,所以消息不会丢失2.高吞吐量:可以轻松实现单机百万级别的并发3.扩展性:扩展性强,还是动态扩展4.多客户端支持:支持多种语言(Java、C、C++、GO、)5.KafkaStreams(一个天生的流处理):在双十一或者销售大屏就会用到这种流处理。使用KafkaStreams可以快速的把销售额统计出来6.安全机制:Kafka进行生产或者消费的时候会</div> </li> <li><a href="/article/1835454543471669248.htm" title="Java:爬虫框架" target="_blank">Java:爬虫框架</a> <span class="text-muted">dingcho</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><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a> <div>一、ApacheNutch2【参考地址】Nutch是一个开源Java实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。包括全文搜索和Web爬虫。Nutch致力于让每个人能很容易,同时花费很少就可以配置世界一流的Web搜索引擎.为了完成这一宏伟的目标,Nutch必须能够做到:每个月取几十亿网页为这些网页维护一个索引对索引文件进行每秒上千次的搜索提供高质量的搜索结果简单来说Nutch支持分</div> </li> <li><a href="/article/1835452402178813952.htm" title="Linux查看服务器日志" target="_blank">Linux查看服务器日志</a> <span class="text-muted">TPBoreas</span> <a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</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> <div>一、tail这个是我最常用的一种查看方式用法如下:tail-n10test.log查询日志尾部最后10行的日志;tail-n+10test.log查询10行之后的所有日志;tail-fn10test.log循环实时查看最后1000行记录(最常用的)一般还会配合着grep用,(实时抓包)例如:tail-fn1000test.log|grep'关键字'(动态抓包)tail-fn1000test.log</div> </li> <li><a href="/article/1835447606348705792.htm" title="C++ lambda闭包消除类成员变量" target="_blank">C++ lambda闭包消除类成员变量</a> <span class="text-muted">barbyQAQ</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/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a> <div>原文链接:https://blog.csdn.net/qq_51470638/article/details/142151502一、背景在面向对象编程时,常常要添加类成员变量。然而类成员一旦多了之后,也会带来干扰。拿到一个类,一看成员变量好几十个,就问你怕不怕?二、解决思路可以借助函数式编程思想,来消除一些不必要的类成员变量。三、实例举个例子:classClassA{public:...intfu</div> </li> <li><a href="/article/1835444959478640640.htm" title="2021 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级C++语言试题 (第三大题:完善程序 代码)" target="_blank">2021 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级C++语言试题 (第三大题:完善程序 代码)</a> <span class="text-muted">mmz1207</span> <a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/csp/1.htm">csp</a> <div>最近有一段时间没更新了,在准备CSP考试,请大家见谅。(1)有n个人围成一个圈,依次标号0到n-1。从0号开始,依次0,1,0,1...交替报数,报到一的人离开,直至圈中剩最后一个人。求最后剩下的人的编号。#includeusingnamespacestd;intf[1000010];intmain(){intn;cin>>n;inti=0,cnt=0,p=0;while(cnt#includeu</div> </li> <li><a href="/article/1835444957423431680.htm" title="《 C++ 修炼全景指南:九 》打破编程瓶颈!掌握二叉搜索树的高效实现与技巧" target="_blank">《 C++ 修炼全景指南:九 》打破编程瓶颈!掌握二叉搜索树的高效实现与技巧</a> <span class="text-muted">Lenyiin</span> <a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a><a class="tag" taget="_blank" href="/search/%E4%BF%AE%E7%82%BC%E5%85%A8%E6%99%AF%E6%8C%87%E5%8D%97/1.htm">修炼全景指南</a><a class="tag" taget="_blank" href="/search/%E6%8A%80%E6%9C%AF%E6%8C%87%E5%8D%97/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><a class="tag" taget="_blank" href="/search/stl/1.htm">stl</a> <div>摘要本文详细探讨了二叉搜索树(BinarySearchTree,BST)的核心概念和技术细节,包括插入、查找、删除、遍历等基本操作,并结合实际代码演示了如何实现这些功能。文章深入分析了二叉搜索树的性能优势及其时间复杂度,同时介绍了前驱、后继的查找方法等高级功能。通过自定义实现的二叉搜索树类,读者能够掌握其实际应用,此外,文章还建议进一步扩展为平衡树(如AVL树、红黑树)以优化极端情况下的性能退化。</div> </li> <li><a href="/article/1835443696431099904.htm" title="笋丁网页自动回复机器人V3.0.0免授权版源码" target="_blank">笋丁网页自动回复机器人V3.0.0免授权版源码</a> <span class="text-muted">希希分享</span> <a class="tag" taget="_blank" href="/search/%E8%BD%AF%E5%B8%8C%E7%BD%9158soho_cn/1.htm">软希网58soho_cn</a><a class="tag" taget="_blank" href="/search/%E6%BA%90%E7%A0%81%E8%B5%84%E6%BA%90/1.htm">源码资源</a><a class="tag" taget="_blank" href="/search/%E7%AC%8B%E4%B8%81%E7%BD%91%E9%A1%B5%E8%87%AA%E5%8A%A8%E5%9B%9E%E5%A4%8D%E6%9C%BA%E5%99%A8%E4%BA%BA/1.htm">笋丁网页自动回复机器人</a> <div>笋丁网页机器人一款可设置自动回复,默认消息,调用自定义api接口的网页机器人。此程序后端语言使用Golang,内存占用最高不超过30MB,1H1G服务器流畅运行。仅支持Linux服务器部署,不支持虚拟主机,请悉知!使用自定义api功能需要有一定的建站基础。源码下载:https://download.csdn.net/download/m0_66047725/89754250更多资源下载:关注我。安</div> </li> <li><a href="/article/1835440673550069760.htm" title="20个新手学习c++必会的程序 输出*三角形、杨辉三角等(附代码)" target="_blank">20个新手学习c++必会的程序 输出*三角形、杨辉三角等(附代码)</a> <span class="text-muted">X_StarX</span> <a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/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/%E5%A4%A7%E5%AD%A6%E7%94%9F/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%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a> <div>示例1:HelloWorld#includeusingnamespacestd;intmain(){coutusingnamespacestd;intmain(){inta=5;intb=10;intsum=a+b;coutusingnamespacestd;intfactorial(intn){if(nusingnamespacestd;voidprintFibonacci(intn){intt</div> </li> <li><a href="/article/1835440294980579328.htm" title="C++八股" target="_blank">C++八股</a> <span class="text-muted">Petrichorzncu</span> <a class="tag" taget="_blank" href="/search/%E5%85%AB%E8%82%A1%E6%80%BB%E7%BB%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/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>这里写目录标题C++内存管理C++的构造函数,复制构造函数,和析构函数深复制与浅复制:构造函数和析构函数哪个能写成虚函数,为什么?C++数据结构内存排列结构体和类占用的内存:==虚函数和虚表的原理==虚函数虚表(Vtable)虚函数和虚表的实现细节==内存泄漏==指针的工作原理函数的传值和传址new和delete与malloc和freeC++内存区域划分C++11新特性C++常见新特性==智能指针</div> </li> <li><a href="/article/1835435758844997632.htm" title="【2022 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级 C++语言试题及解析】" target="_blank">【2022 CCF 非专业级别软件能力认证第一轮(CSP-J1)入门级 C++语言试题及解析】</a> <span class="text-muted">汉子萌萌哒</span> <a class="tag" taget="_blank" href="/search/CCF/1.htm">CCF</a><a class="tag" taget="_blank" href="/search/noi/1.htm">noi</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/c%2B%2B/1.htm">c++</a> <div>一、单项选择题(共15题,每题2分,共计30分;每题有且仅有一个正确选项)1.以下哪种功能没有涉及C++语言的面向对象特性支持:()。A.C++中调用printf函数B.C++中调用用户定义的类成员函数C.C++中构造一个class或structD.C++中构造来源于同一基类的多个派生类题目解析【解析】正确答案:AC++基础知识,面向对象和类有关,类又涉及父类、子类、继承、派生等关系,printf</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>