• ekhtml使用总结


    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
    ");
            return;
        }
        if (data->len >= PRINT_BUFF_SIZE) {
            printf("data->len >= PRINT_BUFF_SIZE
    ");
            return;
        }
        
        strncpy(g_print_buff, data->str, data->len);
        g_print_buff[data->len] = '';
        printf("%s
    ", g_print_buff);
        
        return;
    }
    
    static void simple_data_callback(void *cbdata, ekhtml_string_t *data) {
        printf("enter simple_data_callback
    ");
        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
    ");
        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
    ", attr->isBoolean);
            
            attr = attr->next;
        }
    
        return;
    }
    
    static void end_tag_callback(void *cbdata, ekhtml_string_t *tag) {
        printf("enter end_tag_callback
    ");
        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
    ");
            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
    ");
            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
    
  • 相关阅读:
    高效 告别996,开启java高效编程之门 3-16收集器与预定义收集器概述
    高效 告别996,开启java高效编程之门 3-15实战:流的构建四种形式
    高效 告别996,开启java高效编程之门 3-14常用操作总结与流构建描述
    高效 告别996,开启java高效编程之门 3-13实战:常用终端操作之最大/最小/计数
    高效 告别996,开启java高效编程之门 3-12实战:常用终端操作之查找
    高效 告别996,开启java高效编程之门 3-11实战:常用终端操作之匹配
    cube中的判断类型
    正则表达式解析基本json
    敏捷开发入门教程-----摘抄
    单页面路由样例
  • 原文地址:https://www.cnblogs.com/atskyline/p/7866669.html
Copyright © 2020-2023  润新知