ekhtml是一个高效SAX方式的HTML解析库。

文件说明

官网下载ekhtml-0.3.2.tar.gz文件解压后,内部包括源码、测试文件、文档、编译脚本等。

如需编译成静态库或动态库后进行集成,可以参考INSTALL文件,使用./configure && make命令编译,编译后的库文件生成与src/.libs/中。直接使用库文件,还需要引用include/ekhtml.h文件。

如需进行源码集成,可以复制srcinclude两个文件夹中的文件。

其中ekhtml.h是整个库对外的API,本文档也是基于ekhtml.h进行介绍。

基本类型

typedef struct ekhtml_string_t {
const char *str; /**< Actual string data */
size_t len; /**< Length of the data in `str` */
} ekhtml_string_t;

核心的字符串类型,与nginx的字符串类似采用非NULL结尾的类型定义。

typedef struct ekhtml_attr_t {
ekhtml_string_t name; /**< Name of the attribute */
ekhtml_string_t val; /**< Value of the attribute */
unsigned int isBoolean; /**< True of the attribute is boolean */
struct ekhtml_attr_t *next; /**< Pointer to next attribute in the list */
} ekhtml_attr_t;

属性列表,用于存放标签属性和内容的

typedef struct ekhtml_parser_t ekhtml_parser_t;

解析器对象,可以认为是ekhtml运转的关键。

基本使用方法

  1. 新建parser对象
  2. 给parser配置回调函数(例如script标签回调函数、img标签回调函数)
  3. 将HTML文本输入给parser,parser会根据解析情况调用步骤2注册的回调函数。
  4. 释放parser对象

parser新建与释放

extern ekhtml_parser_t *ekhtml_parser_new(void *cbdata);
extern void ekhtml_parser_destroy(ekhtml_parser_t *parser);

其中void *cbdata将来会传递给回调函数,用于事先具体业务,一般可以使用一个自定义结构体,保存上下文信息,用于将几个回调函数关联起来。关于void *cbdata还有一个动态修改的接口:

extern void ekhtml_parser_cbdata_set(ekhtml_parser_t *parser, void *cbdata);

回调函数类型与注册

支持的回调函数类型有下三种:

typedef void (*ekhtml_data_cb_t)(void *cbdata, ekhtml_string_t *data);
typedef void (*ekhtml_starttag_cb_t)(void *cbdata, ekhtml_string_t *tag, ekhtml_attr_t *attrs);
typedef void (*ekhtml_endtag_cb_t)(void *cbdata, ekhtml_string_t *tag);

以数据<FOO bar="baz">data_to_process</FOO>解析举例:

  • ekhtml_data_cb_t 用于处理文本内容,解析到data_to_process时会调用ekhtml_data_cb_t类型的函数,data参数就是data_to_process
  • ekhtml_starttag_cb_t用于处理起始标签,解析到<FOO bar="baz">时会调用ekhtml_starttag_cb_t类型的函数,tag参数为FOO ,attrs为属性信息bar="baz",数据结构参考上文的ekhtml_attr_t
  • ekhtml_endtag_cb_t用于处理结尾标签,解析到</FOO>时会调用ekhtml_endtag_cb_t类型的函数,tag参数为FOO
  • 三种回调函数类型的cbdata参数,在上文已经说明。

【注意】单闭合标签例如<link rel="stylesheet" />,只会调用starttag不会调用endtag。

函数注册接口有4个:

extern void ekhtml_parser_datacb_set(ekhtml_parser_t *parser, ekhtml_data_cb_t cb);
extern void ekhtml_parser_commentcb_set(ekhtml_parser_t *parser, ekhtml_data_cb_t cb);
extern void ekhtml_parser_startcb_add(ekhtml_parser_t *parser, const char *tag, ekhtml_starttag_cb_t cb);
extern void ekhtml_parser_endcb_add(ekhtml_parser_t *parser, const char *tag, ekhtml_endtag_cb_t cb);

ekhtml_parser_startcb_addekhtml_parser_endcb_add没有特别需要解释的。ekhtml_parser_datacb_setekhtml_parser_commentcb_set都是注册ekhtml_data_cb_t类型的回调函数,但ekhtml_parser_commentcb_set只用于处理注释信息。

【注意】ekhtml_parser_datacb_set是一个扩展性极强的函数,包括非标准标签,标签间的回车换行空格都会调用ekhtml_parser_datacb_set配置的回调函数,具体可以运行下文的demo感受一下。

HTML文本输入

涉及到2个接口

extern void ekhtml_parser_feed(ekhtml_parser_t *parser, ekhtml_string_t *data);
extern int ekhtml_parser_flush(ekhtml_parser_t *parser, int flushall);
  • ekhtml_parser_feed接口允许将数据分多次输入,即使多少次输入的数据在关键位置出现分段。第一次调用输入<FO第二次调用输入0></FOO>,也是可以实现正确解析的。
  • ekhtml_parser_flush此接口与内部实现机制相关,用于立刻刷新内部缓存。ekhtml存在一块内部BUF,feed接口的入参会复制到内部BUF,多次调用feed接口后,内部BUF满之后,ekhtml调用ekhtml_parser_flush进行数据解析。可以手动调用flush立刻进行解析,为了减少解析次数,提升整体性能,建议不需要手动调用flush。
  • flushall参数一般配置为0即可,配置为1会强制刷新未解析完成的内容,上文提到的标签文本被分离的场景将失效。

DEMO代码

#include <stdio.h>
#include <string.h>
#include "ekhtml.h" #define PRINT_BUFF_SIZE (1024)
static char g_print_buff[PRINT_BUFF_SIZE]; static void print_ekhtml_string(ekhtml_string_t *data) {
if (data == NULL) {
printf("data == NULL\n");
return;
}
if (data->len >= PRINT_BUFF_SIZE) {
printf("data->len >= PRINT_BUFF_SIZE\n");
return;
} strncpy(g_print_buff, data->str, data->len);
g_print_buff[data->len] = '\0';
printf("%s\n", g_print_buff); return;
} static void simple_data_callback(void *cbdata, ekhtml_string_t *data) {
printf("enter simple_data_callback\n");
printf("data = ");
print_ekhtml_string(data); return;
} static void start_tag_callback(void *cbdata, ekhtml_string_t *tag, ekhtml_attr_t *attrs) {
ekhtml_attr_t *attr; printf("enter start_tag_callback\n");
printf("tag = ");
print_ekhtml_string(tag); attr = attrs;
while(attr != NULL) {
printf("attr->name = ");
print_ekhtml_string(&attr->name);
printf("attr->val = ");
print_ekhtml_string(&attr->val);
printf("attr->isBoolean = %u\n", attr->isBoolean); attr = attr->next;
} return;
} static void end_tag_callback(void *cbdata, ekhtml_string_t *tag) {
printf("enter end_tag_callback\n");
printf("tag = ");
print_ekhtml_string(tag); return;
} int main(int argc, char *argv[]) {
FILE *file;
char file_buf[1024];
size_t nread; ekhtml_parser_t *parser;
ekhtml_string_t html_str; parser = ekhtml_parser_new(NULL);
if (parser == NULL) {
printf("ekhtml_parser_new fail\n");
return -1;
} ekhtml_parser_datacb_set(parser, simple_data_callback);
ekhtml_parser_commentcb_set(parser, simple_data_callback);
ekhtml_parser_startcb_add(parser, "script", start_tag_callback);
ekhtml_parser_endcb_add(parser, "script", end_tag_callback); if ((file = fopen("data.html", "r")) == NULL) {
printf("open data.html fail\n");
return -1;
}
while ((nread = fread(file_buf, 1, sizeof(file_buf), file)) > 0) {
html_str.str = file_buf;
html_str.len = nread;
ekhtml_parser_feed(parser, &html_str);
ekhtml_parser_flush(parser, 0);
}
fclose(file); ekhtml_parser_destroy(parser);
return 0;
}

编译脚本:

gcc -g -o demo demo.c libekhtml.a

ekhtml使用总结的更多相关文章

随机推荐

  1. 省赛i题/求1~n内所有数对(x,y),满足最大公约数是质数的对数

    求1~n内所有数对(x,y),gcd(x,y)=质数,的对数. 思路:用f[n]求出,含n的对数,最后用sum[n]求和. 对于gcd(x,y)=a(设x<=y,a是质数),则必有gcd(x/a ...

  2. WEB学习-CSS基础选择器

    基础选择器 标签选择器 就是标签的名字. • <h1>前端与移动开发<span>1期班</span>基础班</h1> css: • <style ...

  3. Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第104篇原创 文章工程:* JDK 1.8* M ...

  4. 使用css绘制六边形

    用css绘制六边形需要使用到三个容器,分别用于绘制六边形的三个部分,如下图所示: 接下来,就是代码了: CSS: ;;border-top: 30px solid #6c6;border-left:  ...

  5. Maven的相关问题(一)——settings.xml配置详解

    工作中第一次正式接触maven,虽然之前在学习时有遇到过,但是对于maven的认识和理解确实太浅薄,仅仅局限于机械式的操,纸上得来终觉浅,绝知此事要躬行···古人诚不欺我也~ 下面先贴一个找到的一个非 ...

  6. db2 获取自增主键的方法

    1.用SEQUENCES方式 建表语句 CREATE TABLE TEST1( PKEY INTEGER NOT NULL, NAME VARCHAR(100), SEX VARCHAR(100), ...

  7. SSM框架笔记

    配置 Project结构 SpringMVC启用 Spring MVC配置 Spring自己主动扫描 getBean的方法 SpringMVC与Struts2的差别 Log4j 拦截器与过滤器 文件U ...

  8. postgresql 导出建表语句的方法-类似describe table

    https://www.youtube.com/watch?v=PMfcsYzj-9M  这个视频不错, The Definitive Guide to Object-Oriented JavaScr ...

  9. lua 异常 错误处理 pcall

    lua 错误处理 匿名函数 if pcall(function () local s=object.data[1]['type'] end) then return object.data[1]['t ...

  10. react 实现pure render的时候,bind(this)隐患

    react 实现pure render的时候,bind(this)隐患 export default class Parent extends Component { ... render() { c ...