hparser document

github : https://github.com/chloro-pn/...

本篇作为hparser的文档,主要分为三部分进行说明。

hparser查询接口介绍

使用类hparser解析xhtml文件:

#include "hparser.h"
#include <exception>
#include <iostream>
int main() {
 //content is a std::string object.
 //content 存储了utf8编码的xhtml文本。
 //在构造函数过程中进行解析,解析失败会抛出parser_error异常。
 try {
 hparser doc(content);
 }
 catch(const std::exception& e) {
 std::cout << e.what() << std::endl;
 }
}

类hparser有以下接口函数:
string_type global_notes() const;
此函数的作用是返回不在顶层标签内的注释信息,DOCTYPE declaration等文本。

std::shared_ptr<element_type> get_root() const;
返回xhtml文本基于dom模型的顶层元素,即<html>...</html>元素。

std::vector<std::shared_ptr<element_type>> find_tag(std::string str) const;
std::vector<std::shared_ptr<element_type>> find_attr(std::string str) const;
std::vector<std::shared_ptr<element_type>> find_content(std::string str) const;

三个查询函数,分别在xhtml文本中按照tag信息,attr信息和content信息查询,返回符合查询条件的element集合。输入的std::string可以是一个regex pattern,内部会通过std::regex进行匹配。最好保证xhtml是ascii编码,关于std::regex和unicode编码的问题见以下链接:https://www.zhihu.com/questio...
如果你确实需要处理ascii之外的扩展字符并需要用正则匹配,或者上述接口的查询能力不足,使用以下接口:

std::vector<std::shared_ptr<element_type>> find(std::function<bool(std::shared_ptr<element_type> each)> func) const;

find函数接收一个可调用对象,输入参数为指向element_type的共享指针,你可以根据该元素的信息确定是否查询成功,如果是则返回true,否则返回false。当然,你可以在该函数中将元素的记录文本由utf8转为你需要的编码,然后用正则表达式匹配并确定是否成功。

auto check_func = [](std::shared_ptr<element_type> node)->bool {
 std::u32string tmp = encode_cast(node->content());
 bool found = regex_by_encode(tmp, yourpattern);
 return found;
};
auto result = h.find(chekc_func);

上述代码不是合法的c++代码,仅作为伪代码展示。

类型element_type介绍

element_type代表了xml/html基于文档对象模型(DOM)的元素概念,元素之间形成树结构,每个元素拥有父元素(根元素除外),可选的子元素,标签tag,文本content等信息。该类型具有以下接口:

//公开的kv_type类型,此类型代表元素属性的类型,是一个key-value对,此类型的对象保证拥有公开可访问的数据成员key_和value_。
using kv_type = inner_kv_type;
//返回此元素的标签
string_type tag() const;
//返回此元素内的内容,但不包括子元素的内容
string_type content() const;
//返回属性的数量
size_t attrs_size() const;
//返回子元素的数量
size_t childs_size() const;
//按照index返回属性,index从0开始
kv_type get_attr(size_t index) const;
//按照index返回子元素,index从0开始
std::shared_ptr<hparser::element_type> get_child(size_t index) const;
//返回所有属性
std::vector<kv_type> get_all_attrs() const;
//返回所有子元素
std::vector<std::shared_ptr<hparser::element_type>> get_all_childs() const;
//[]运算符重载,根据属性的key访问value,如果key不存在则返回空的value ""。
string_type operator[](string_type str) const;
//判断此元素是否为根元素。
bool root() const;
//返回此元素的父元素,如果此元素为根元素,则返回空的std::shared_ptr。
std::shared_ptr<hparser::element_type> parent() const;

结合使用find接口与element_type类,你将会获得非常强大灵活的查询能力,几乎可以实现任意复杂的查询条件。例如:

auto check_func = [](std::shared_ptr<element_type> node)->bool {
 if(node->root() == false && node->parent()->tag() == u8"div") {
 if(node->tag() == u8"a" && (*node)[u8"href"] != u8"" && node->attrs_size() == 1) {
 return true;
 }
 }
 return false;
};
auto result = h.find(chekc_func);

这个查询函数要求返回元素的父元素标签为div,本元素标签为a且只含有属性“href”。
体会到find接口强大的查询能力了么?你可以根据element_type所拥有的信息定制任何查询条件。接下来看第三部分,编码与正则匹配,这会进一步提升find接口的查询能力:)

utf8_to_utf32/utf32_to_utf8接口介绍

c++处理编码真是难,c++标准库中的string只是个char array,其并不含有编码信息,而一个合理的正则匹配应该是codepoint-by-codepoint的,因此std::regex能够处理ascii码,但对扩展编码则爱莫能助。虽然标准库又提供了wchar_t和std::wregex,但其在不同平台上其占用字节大小居然不同。。。在这种情况下,首先我们不能手工编码然后存储wchar_t,因为其占用字节大小不定,其次,标准库提供的std::string和std::wstring相互转换的组件在c++17标准中被废除。现在std::string和std::wstring完全成了两套东西,相互之间的转换已经不能。

c++11提出了两种新的字符存储类型char16_t和char32_t,其具有确定的大小。本来以为曙光来临,将所有外部输入编码都转化为utf32在内部表示,然后用std::basic_regex<char32_t>进行正则处理,目前为止的unicode-code-point都能用一个wchar32_t装下,天下太平了。然而std::basic_regex模板类不直接提供char32_t的支持,需要实现std::regex_traits<char32_t>(因为char32_t只是个更大的存储单位,并不带有编码信息,而个人认为正则处理应该建立在具体的编码之上实现。)。见链接:https://stackoverflow.com/que...

机智的我停止填这个无底深坑,转而提供utf8-utf32的转换接口,如果你有支持utf32的正则库,则可以通过此接口转换编码然后做正则匹配。

//将c转换为本机字节序,en是c当前的字节序
//endian是一个enum class,该类型的对象可被设置为
//endian::little_endian or endian::big_endian。
char32_t endian_cast(char32_t c, endian en);
//将src中的utf8编码转换为utf32编码,en为输出参数的字节序,默认为本机字节序。
size_t utf8_to_utf32(const std::string& src, std::u32string& dst, endian en = local_endian().get());
//将str中的utf32编码转换为utf8编码,en为输入参数的字节序,默认为本机字节序。
size_t utf32_to_utf8(const std::u32string& src, std::string& dst, endian en = local_endian().get());

上述两个接口的返回值为第一个转换失败的文本的index,转换成功时应满足return_index == src.size()。否则src[return_index]及其之后的输入文本转换失败。
综上,你可以在查询函数中将utf8编码转换为utf32编码,然后利用支持char32_t的正则表达式库做正则匹配。

作者:p__n原文地址:https://segmentfault.com/a/1190000021749001

%s 个评论

要回复文章请先登录注册