ekhtml使用总结
文件说明
官网下载ekhtml-0.3.2.tar.gz文件解压后,内部包括源码、测试文件、文档、编译脚本等。
如需编译成静态库或动态库后进行集成,可以参考INSTALL文件,使用./configure && make
命令编译,编译后的库文件生成与src/.libs/中。直接使用库文件,还需要引用include/ekhtml.h
文件。
如需进行源码集成,可以复制src
与include
两个文件夹中的文件。
其中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运转的关键。
基本使用方法
- 新建parser对象
- 给parser配置回调函数(例如script标签回调函数、img标签回调函数)
- 将HTML文本输入给parser,parser会根据解析情况调用步骤2注册的回调函数。
- 释放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_add
与ekhtml_parser_endcb_add
没有特别需要解释的。ekhtml_parser_datacb_set
和ekhtml_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使用总结的更多相关文章
随机推荐
- raspberry pi系统安装
1.格式化SD卡,用SDFormatter 2.解压下载的操作系统 3.复制操作系统到SD卡(要放在根目录,把最外面的文件夹路径去掉) 4.把SD卡插入raspberry pi,接上电源 5.在启动界 ...
- iOS的应用程序实现之间的内容分享
前言 我们在iOS的平台上想要实现不同应用之间的内容分享一般有几种常用方式: 一种第的英文通过AirDrop实现不同设备的应用之间文档和数据的分享; 第二种是给每个应用程序定义一个URL方案,通过访问 ...
- python内存泄露诊断过程记录pyrasite
工具:pyrasite;包含三个命令行 pyrasite / pyrasite-shell / pyrasite-memory-viewer 安装:gdb meliae urwid 说明:Pyrasi ...
- BUPT 2012复试机考 1T
题目描述 大家都知道,数据在计算机里中存储是以二进制的形式存储的. 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的. 你能帮帮 ...
- gulp配置 - PC
初始化目录结构如下(图片看不清可以拖到桌面或者直接CTRL+鼠标滚轮进行观看) 开发环境示例: 上线环境示例: gulpfile.js(详解版) (2018-3-28)添加了scss处理(去除了les ...
- cocos2d-x 3.0 引用第三方库 及编译成apk时android mk文件写法
cocos2d-x 3.0 中.假设你须要使用CocosStudio.Extensions扩展库 等等.都须要自己手动加入. 加入过程例如以下:(比方说如今我要加入libExtensions,libC ...
- 走入asp.net mvc不归路:[2]控制器概览
asp.net mvc中最灵活的地方就是控制器,这里可以验证数据,可以跳转视图,还可以访问数据库等等.所以,我们要先从这里说起. 1 控制器就是继承了Controller的类,一般来说,类名后面都会增 ...
- 理解和使用WPF 验证机制
博客 学院 下载 更多 写博客 发布Chat 登录注册 理解和使用WPF 验证机制 原创 2013年06月20日 11:15:37 7404 首先建立一个demo用以学习和实验WPF Data Val ...
- Linux fork函数具体图解-同一时候分析一道腾讯笔试题
原创blog.转载请注明出处 头文件: #include<unistd.h> #include<sys/types.h> 函数原型: pid_t fork( void); (p ...
- gradle配置远程仓库(以及使用本地maven仓库)
allprojects{ repositories { mavenLocal() def REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content ...