9.综合调试|输入不能存在空格|desc存在None|输出权值和ID|函数重名|修改文件名|权值和实际关键词出现次数(C++)
输入不能存在空格
目前输入的关键词时每隔一空格内容分别进行搜索,大部分时候我们都是将一串包含空格的内容直接进行搜索,需要将代码改进。
将cin换为fgets
#include "searcher.hpp"
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
const std::string input = "data/raw_html/raw.txt";
int main()
{
//for test
ns_searcher::Searcher *search = new ns_searcher::Searcher();
search->InitSearcher(input);
std::string query;
std::string json_string;
char buffer[1024];
while(true){
std::cout << "Please Enter You Search Query# ";
fgets(buffer, sizeof(buffer)-1, stdin);
buffer[strlen(buffer) - 1] = 0;
query = buffer;
search->Search(query, &json_string);
std::cout << json_string << std::endl;
}
return 0;
}
desc存在None
搜索出来的结果,有部分desc的内容时None
std::string GetDesc(const std::string &html_content, const std::string &word)
{
//找到word在html_content中的首次出现,然后往前找50个字节(如果没有,就从begin开始),往后找100个字节(如果没有,就截取到end)
//截取出这部分内容
const std::size_t prev_step = 50;
const std::size_t next_step = 100;
//1.找到首次出现
std::size_t pos = html_content.find(word);
if(pos == std::string::npos){
return "None1"; //不可能发生
}
//2.获取start,end
std::size_t start = 0;
std::size_t end = html_content.size() - 1;
//如果之前有50+字符,就更新开始位置
if(pos - prev_step > start) start = pos - prev_step;
if(pos + next_step < end) end = pos + next_step;
//3.截取子串,return
if(start >= end) return "None2";
return html_content.substr(start, end - start);
}
给None的两种情况加上脚标
None1和2都有
因为pos是无符号整数,减去step过了0,成为负数,其实是变为一个特别大的值,此时就超过了start,远大于start,条件成立,更新start,start变为一个特别大的值;这时start有可能大于end,于是返回None2
if(pos > start + prev_step) start = pos - prev_step;
将-变为+,转换为两个正数的比较
std::string GetDesc(const std::string &html_content, const std::string &word)
{
//找到word在html_content中的首次出现,然后往前找50个字节(如果没有,就从begin开始),往后找100个字节(如果没有,就截取到end)
//截取出这部分内容
const std::size_t prev_step = 50;
const std::size_t next_step = 100;
//1.找到首次出现
std::size_t pos = html_content.find(word);
if(pos == std::string::npos){
return "None1"; //不可能发生
}
//2.获取start,end,std::size_t 无符号整数
std::size_t start = 0;
std::size_t end = html_content.size() - 1;
//如果之前有50+字符,就更新开始位置
if(pos > start + prev_step) start = pos - prev_step;
if((int)pos < (int)(end - next_step)) end = pos + next_step;
//3.截取子串,return
if(start >= end) return "None2";
return html_content.substr(start, end - start);
}
None2修改完成
在word里面保存的对应的搜索关键字,都变为小写。
索引关键字,搜索关键字都是小写
但是在获取摘要的时候,用的是原始的网页内容,提炼摘要的word是已经转为小写的词
实际在获取摘要,find的时候,就匹配不到了,因为fine不是忽略大小写的
而且string在实际搜索的时候,也没有忽略大小写的匹配方式
并且不能直接将原始网页全部变为小写
可以使用c++的search
std::string GetDesc(const std::string &html_content, const std::string &word)
{
//找到word在html_content中的首次出现,然后往前找50个字节(如果没有,就从begin开始),往后找100个字节(如果没有,就截取到end)
//截取出这部分内容
const int prev_step = 50;
const int next_step = 100;
//1.找到首次出现
auto iter = std::search(html_content.begin(), html_content.end(), word.begin(), word.end(), [](int x, int y){
return (std::tolower(x) == std::tolower(y));
});
if(iter == html_content.end()){
return "None1";
}
int pos = std::distance(html_content.begin(), iter);
//2.获取start,end,std::size_t 无符号整数
int start = 0;
int end = html_content.size() - 1;
//如果之前有50+字符,就更新开始位置
if(pos > start + prev_step) start = pos - prev_step;
if(pos < end - next_step) end = pos + next_step;
//3.截取子串,return
if(start >= end) return "None2";
return html_content.substr(start, end - start);
}
None1修改完成
输出权值和doc_id
searcher.hpp
Json::Value root;
for(auto &item : inverted_list_all){
ns_index::DocInfo * doc = index->GetForwardIndex(item.doc_id);
if(nullptr == doc){
continue;
}
Json::Value elem;
elem["title"] = doc->title;
elem["desc"] = GetDesc(doc->content, item.word); //content是文档的去标签的结果,但是不是我们想要的,我们要的是一部分
elem["url"] = doc->url;
//for debug
elem["id"] = (int)item.doc_id; //int->string
elem["weight"] = item.weight;
root.append(elem);
}
是按照权值大小降序排列的
打开这个网页查看
函数重名
util.hpp
class StringUtil{
public:
static void Split(const std::string &target, std::vector<std::string> *out, const std::string &sep)
{
//boost split
boost::split(*out, target, boost::is_any_of(sep), boost::token_compress_on);
}
};
把StringUtil的函数名改为Split
index.hpp
DocInfo *BuildForwardIndex(const std::string &line)
{
//1. 解析line,字符串切分
//line -> 3 string, title, content, url
std::vector<std::string> results;
const std::string sep = "\3"; //行内分隔符
ns_util::StringUtil::Split(line, &results, sep);
//ns_util::StringUtil::CutString(line, &results, sep);
if(results.size() != 3){
return nullptr;
}
//2. 字符串进行填充到DocIinfo
DocInfo doc;
doc.title = results[0]; //title
doc.content = results[1]; //content
doc.url = results[2]; ///url
doc.doc_id = forward_index.size(); //先进行保存id,在插入,对应的id就是当前doc在vector中的下标!
//3. 插入到正排索引的vector
forward_index.push_back(std::move(doc)); //doc,html文件内容
return &forward_index.back();
}
把index的BuildForwardIndex的调用改为Split
修改文件名
makefile
PARSER=parser
DUG=debug
cc=g++
.PHONY:all
all:$(PARSER) $(DUG)
$(PARSER):parser.cc
$(cc) -o $@ $^ -lboost_system -lboost_filesystem -std=c++11
$(DUG):debug.cc
$(cc) -o $@ $^ -ljsoncpp -std=c++11
.PHONY:clean
clean:
rm -f $(PARSER) $(DUG)
将server.cc改名为debug.cc
mv server.cc debug.cc
权值和实际关键词出现次数不符合
会多计算也会少计算权值
可能分词工具在分词的时候会少分词
如果目标关键词在尖括号内,可能会当成标签处理,不会计入
把整个文件拿到内存
去标签之后,先拿到标题
对整个文件进行去标签,其中是包括标签的
实际,如果一个词在title中出现,一定会被当标题和当内容分别统计一次
index.hpp
bool BuildInvertedIndex(const DocInfo &doc)
{
//DocInfo{title, content, url, doc_id}
//word -> 倒排拉链
struct word_cnt{
int title_cnt;
int content_cnt;
word_cnt():title_cnt(0), content_cnt(0){}
};
std::unordered_map<std::string, word_cnt> word_map; //用来暂存词频的映射表
//对标题进行分词
std::vector<std::string> title_words;
ns_util::JiebaUtil::CutString(doc.title, &title_words);
if(doc.doc_id == 8713){
for(auto &s : title_words){
std::cout << "title: " << s << std::endl;
}
}
//对标题进行词频统计
for(std::string s : title_words){
boost::to_lower(s); //需要统一转化成为小写
word_map[s].title_cnt++; //如果存在就获取,如果不存在就新建
}
//对文档内容进行分词
std::vector<std::string> content_words;
ns_util::JiebaUtil::CutString(doc.content, &content_words);
if(doc.doc_id == 8713){
for(auto &s : content_words){
std::cout << "content: " << s << std::endl;
}
}
//对内容进行词频统计
for(std::string s : content_words){
boost::to_lower(s);
word_map[s].content_cnt++;
}
#define X 10
#define Y 1
//Hello,hello,HELLO
for(auto &word_pair : word_map){
InvertedElem item;
item.doc_id = doc.doc_id;
item.word = word_pair.first;
item.weight = X*word_pair.second.title_cnt + Y*word_pair.second.content_cnt; //相关性
InvertedList &inverted_list = inverted_index[word_pair.first];
inverted_list.push_back(std::move(item));
}
return true;
}
debug.cc
#include "searcher.hpp"
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
const std::string input = "data/raw_html/raw.txt";
int main()
{
//for test
ns_searcher::Searcher *search = new ns_searcher::Searcher();
search->InitSearcher(input);
//std::string query;
//std::string json_string;
//char buffer[1024];
//while(true){
// std::cout << "Please Enter You Search Query# ";
// fgets(buffer, sizeof(buffer)-1, stdin);
// buffer[strlen(buffer) - 1] = 0;
// query = buffer;
// search->Search(query, &json_string);
// std::cout << json_string << std::endl;
//}
return 0;
}
生成了内容和分词,但是很难观察
创建一个log.txt
touch log.txt
将debug的内容重定向到log.txt当中
./debug > log.txt
共有7000行内容
查找split