1. 文件枚举
我们要使用到 boost 里面 filesystem 这个命名空间里面的函数,所以我在这里先给它取个别名。然后我们把 src_path 里面的路径交给 root_path。接着我们判断这个路径是否存在,如果不存在那就直接结束代码。接着我们通过迭代器循环的方式来对 root_path 里面的每一个文件。
第一个 if 用来判断是否是普通文件,第二个 if 来判断文件的扩展名是否为 .html,接着走到最后就书面是扩展名为 .html 的普通文件。然后我们就把它的路径转化为 string 类型。
注意:在这里不可以把 .string() 换成 to_string()。这是因为:
.string()是std::filesystem::path类的成员函数,专门用于将路径对象转换为std::string类型的字符串(返回路径的字符串表示)。to_string()是 C++ 标准库中的全局函数(或针对基础类型的重载),用于将数值类型(如int、double等)转换为字符串,不能直接用于路径对象。
bool EnumFile(const std::string &src_path, std::vector<std::string> *file_list) {
namespace fs = boost::filesystem;
fs::path root_path(src_path);
if (!fs::exists(root_path)) {
std::cout << src_path << " is not exist" << std::endl;
return false;
}
fs::recursive_directory_iterator end;
for (fs::recursive_directory_iterator iter(root_path); iter != end; iter++) {
if (!fs::is_regular_file(*iter)) continue;
if (iter->path().extension() != ".html") continue;
file_list->push_back(iter->path().string());
}
return true;
}
2. 内容解析
2.1 读取并解析(去标签化)
这个就是去标签化的函数,通过一步步对文件的解析分别提出 title 和 content,然后构建出 url,接下来就是把 doc 里面的内容交给 results。
bool ParseHtml(const std::vector<std::string> &files_list, const std::string &src_path, std::vector<DocInfo_t> *results) {
for (const std::string &file : files_list) {
std::string tr;
if (!ns_util::FileUtil::ReadFile(file, &tr)) continue;
DocInfo doc;
if (!ParseTitle(tr, &doc.title)) continue;
if (!ParseContent(tr, &doc.content)) continue;
if (!ParseUrl(file, src_path, &doc.url)) continue;
results->push_back(doc);
}
return true;
}
2.2 提取 title
因为网页的标题一般是 <title>XXXXXXXXXXXXXXXXXXX</title>,然后我们实际上只要 XXXXXXXXXXXXXXXXXXXXX 这个部分,所以我们通过找位置的方式来确定实际想要的内容的位置,然后把它全部获取。
static bool ParseTitle(std::string &file, std::string *title) {
std::size_t begin = file.find("<title>");
if (begin == std::string::npos) return false;
std::size_t end = file.find("</title>");
if (end == std::string::npos) return false;
begin += std::string("<title>").size();
if (begin > end) return false;
*title += file.substr(begin, end - begin);
return true;
}
2.3 提取 content
我们在这里先创建一个 enum 类型的结构体,用来判断是标签还是我们想要的内容。
然后通过迭代器和 switch 判断的方式,来把所有的 <>的部分去掉。如果是在标签内就一直跳过,如果遇到 > 就判断下一个是不是 <,不是的话就代表是我们想要的内容。
static bool ParseContent(std::string &file, std::string *content) {
typedef enum status {
LABEL,
CONTENT
} judge;
judge s = LABEL;
for (char c : file) {
switch (s) {
case LABEL:
if (c == '>') s = CONTENT;
break;
case CONTENT:
if (c == '<') s = LABEL;
else {
if (c == '\n') c = ' ';
*content += c;
}
break;
default:
break;
}
}
return true;
}
2.4 构建 url
用于构建完整 URL 的函数,其作用是将本地文件路径转换为对应的在线 URL 地址。
建立本地文件与在线文档的对应关系。通过将本地文件的相对路径(相对于 src_path)拼接到固定的 URL 头后面,实现从本地文件路径到在线文档 URL 的转换。
static bool ParseUrl(const std::string &file_path, const std::string &src_path, std::string *url) {
std::string url_head = "https://www.boost.org/doc/libs/1_89_0/doc/html";
std::string url_tail = file_path.substr(src_path.size());
*url = url_head + url_tail;
return true;
}
3. 结果保存
先以二进制的方式打开 output,接着通过迭代器访问 results 的方式把处理完的信息一段一段的放入临时创建的 out_string 里面,接着当收集完一个网页的信息后在放入 output 里面。
最后关闭 out,完成所有的读取。
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &output) {
std::ofstream out(output);
if (!out.is_open()) {
std::cout << "open " << output << " failed!" << std::endl;
return false;
}
for (auto &item : results) {
std::string out_string;
out_string += item.title;
out_string += '\3';
out_string += item.content;
out_string += '\3';
out_string += item.url;
out_string += '\n';
out.write(out_string.c_str(), out_string.size());
}
out.close();
return true;
}

