• 非标准的xml解析器的C++实现:二、解析器的基本构造:语法表


    解析器的目的:一次从头到尾的文本遍历,文本数据 转换为 xml节点数据。

    这其实是全世界所有编程语言编译或者转换为虚拟代码的基础,学会这种方法,发明一种编程语言其实只是时间问题,当然了,时间也是世界上最值钱的玩意儿。

    很多人可能第一时间会想到:

    for (int i = 0; i < len; i++) {
        char c = str[i];
        switch (c) {
        case '<':
            ...
            break;
        case '>':
            ...
            break;
        ...
        }
    }

    大方向其实就是从这里延申出去的,但这样的方法,有一个缺陷,当需要判断的字符太多的时候,代码会很长很长,其中再参杂着各种逻辑之后,代码就不具备可维护性了。

    那么就要找到一种方法是可以很高效,并且能同时判断多个字符:语法表 就是为了解决这个问题而诞生的。

    什么是语法表?

    本质上,语法表只是一个数组,就如同下面这个一样:

    static unsigned short xml_char_syntax[] = {
                0,0,0,0,0,0,0,0,0,8,8,0,0,8,0,0,
                0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                8,64,4,0,0,0,0,4,0,0,0,0,0,8208,16,128,
                1552,1552,1552,1552,1552,1552,1552,1552,1552,1552,0,256,1,2048,2,0,
                0,1072,1072,1072,1072,1072,1072,48,48,48,48,48,48,48,48,48,
                48,48,48,48,48,48,48,48,48,48,48,4096,0,0,0,48,
                0,1072,1072,1072,1072,1072,1072,48,48,48,48,48,48,48,48,48,
                48,48,48,48,48,48,48,48,48,48,48,0,0,0,0,0,
            };

    语法表中的数值是根据字符的语义来定义的,采用 或运算 的方式进行叠加(也可以理解为 加法)

    语义又是一种什么东西呢?

    语义的本质就是字符状态,再简单说,它就是一些数字:

    比如大写字母 A

    1、它可以在xml中作为标签名或属性名的第一个字符

    2、也可以存在于标签名或属性名的中间

    3、它可以是HEX(16进制数字)

    那么在创建A的语法值时,就应该:xml_char_syntax['A'] = FCONAME | NAME | HEX;

    FCONAME,NAME, HEX 就是一些常量,可以使用宏定义或者 static constexpr 来定义他们

    定义他们的基本规则:

    每一种语义定义必须占用一个二进制位

    例如:

    #define FCONAME 1

    #define NAME 2

    #define HEX 4

    FCONAME  NAME HEX都是可以利用到多个字符中的,例如:

    for(int i = 'A'; i <= 'Z'; i++){
        xml_char_syntax[i] = FCONAME | NAME ;
        xml_char_syntax[i + 0x20] = FCONAME | NAME ;
        if(i <= 'F') {
            xml_char_syntax[i] |= HEX;
            xml_char_syntax[i + 0x20] |= HEX;
        }
    }

    这样子,字母A-Z a-z都已经被定义为名称首字符,名称中间字符,A-F a-f同时是被允许成为HEX。

    基于这个思路,就可以把解析一种预定格式的文本所需要综合判断,都定义为语义,使用语义创建语法表

    最终,将switch case取代。

    char c = str[i];

    unsigned short s = xml_char_syntax[(unsigned char)c];

    if(!(s & HEX)) printf("字符:%c 不是一个16进制数字字符。");

    本节附带的C/C++知识点:这段跟主题无关,所以用淡一点的文字。

    switch case其实是分支逻辑中性能最高的东西,因为它和if else if是有本质的区别的。

    if else if会被编译成很多个二进制层面的比较。

    而switch case则会被编译成根据数值跳转。

    也就是不管有多少个case,以intel x86汇编为例,它最终都会被编译为:

    jmp [reg + offset] 的形式,

    reg(寄存器)指向程序数据段中的一片内存,这片内存里储存着关于这个switch的各个跳转地址,offset就是根据case指定的值来的。

    看到这里,是不是能发现,语法表其实和switch case是一个道理,本质上都是数据段中储存了数据用于判断跳转,只不过语法表由我们自己实现,我们能掌控更多细节,能分开四处使用。

    嗯。。其实我就是想让看这里的人看着累。。。感谢我吧。。哈哈。。

    未完待续。。。

  • 相关阅读:
    控件与布局
    高性能mysql笔记 第一章 mysql架构
    简单排序——冒泡,选择,插入
    spring boot的默认配置
    nginx配置 负载均衡
    nginx+tomcat反向代理
    fiddler(4)安装--L
    fiddler(3)http协议-响应报文--L
    fiddler(2)http协议-请求报文--L
    Fiddler(1)简介--L
  • 原文地址:https://www.cnblogs.com/babypapa/p/11804612.html
Copyright © 2020-2023  润新知