解析器的目的:一次从头到尾的文本遍历,文本数据 转换为 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是一个道理,本质上都是数据段中储存了数据用于判断跳转,只不过语法表由我们自己实现,我们能掌控更多细节,能分开四处使用。

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

未完待续。。。

非标准的xml解析器的C++实现:二、解析器的基本构造:语法表的更多相关文章

  1. 非标准的xml解析器的C++实现:三、解析器的初步实现

    如同我之前的一篇文章说的那样,我没有支持DTD与命名空间, 当前实现出来的解析器,只能与xmlhttp对比,因为chrome浏览器解析大文档有bug,至于其他人实现的,我就不一一测试了,既然都决定自己 ...

  2. 非标准的xml解析器的C++实现:一、思考基本数据结构的设计

    前言: 我在C++项目中使用xml作为本地简易数据管理,到目前为止有5年时间了,从最初的全文搜索标签首尾,直到目前项目中实际运用的类库细致到已经基本符合w3c标准,我一共写过3次解析器,我自己并没有多 ...

  3. 3.非标准的NDEF格式数据解析--IsoDep

    1.使用目的:正常开发是针对NDEF格式数据进行开发,但实际情况并非如此,以厦门公交卡为例,厦门公交卡保存的是非NDEF格式数据.其类型是IsoDep类型. 2.非标准的NDEF格式数据流程:当厦门公 ...

  4. Python解析非标准JSON(Key值非字符串)

    采集数据的时候经常碰到一些JSON数据的Key值不是字符串,这些数据在JavaScript的上下文中是可以解析的,但在Python中,没有该部分数据的上下文,无法采用json.loads(JSON)的 ...

  5. python之读取配置文件模块configparser(三)高级使用---非标准配置文件解析

    非标准配置文件也是经常使用的,如何使用configparser来解析? 这要从configparser本身解析结构来说,configparser包含section和option,非标准配置文件只有op ...

  6. 《Mybatis 手撸专栏》第9章:细化XML语句构建器,完善静态SQL解析

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你只是在解释过程,而他是在阐述高度! 如果不是长时间的沉淀.积累和储备,我一定也没有 ...

  7. [python标准库]XML模块

    1.什么是XML XML是可扩展标记语言(Extensible Markup Language)的缩写,其中的 标记(markup)是关键部分.您可以创建内容,然后使用限定标记标记它,从而使每个单词. ...

  8. Android程序解析XML文件的方法及使用PULL解析XML案例

    一.一般解析XML文件的方法有SAX和DOM.PULL (1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信 ...

  9. Wireshark分析非标准端口号流量

    Wireshark分析非标准端口号流量 2.2.2  分析非标准端口号流量Wireshark分析非标准端口号流量 应用程序运行使用非标准端口号总是网络分析专家最关注的.关注该应用程序是否有意涉及使用非 ...

随机推荐

  1. 修炼Servlet

    修炼Servlet 一.Servlet简单认识 1.Servlet是什么 Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的 ...

  2. uni-app(Vue)中(picker)用联动(关联)选择以至于完成某些功能

    如下图所示,在项目中需求是通过首先选择学生的专业,选好之后在每个专业下面选择对应的学期,每个学期有对应的学费,因此就需要联动选择来实现这一功能. 以下仅展示此功能主要代码: <div class ...

  3. fiddler 手机+浏览器 抓包

    用fiddler对手机上的程序进行抓包   前提: 1.必须确保安装fiddler的电脑和手机在同一个wifi环境下 备注:如果电脑用的是台式机,可以安装一个随身wifi,来确保台式机和手机在同一wi ...

  4. docker容器命令(一)

    容器命令 创建容器:docker run 参数: -it 交互 -d 后台 –name 容器名 -p 主机端口:容器端口 (主机端口映射到docker端口) docker run --name cen ...

  5. Jquery的常用使用方法

    1.获取单个checkbox选中项(三种写法)$("input:checkbox:checked").val()或者$("input:[type='checkbox']: ...

  6. .Net Core微服务——Ocelot(3):超时、熔断、限流

    基本概念 超时.熔断.限流听起来好像很远,但实际上用在方方面面.很多人可能还搞不懂熔断是做什么,其实可以把熔断理解为一种防护措施.做个假设,在微服务体系下,某个下游服务响应很慢,然后随着时间推移,会有 ...

  7. 【死磕 NIO】— 深入分析Buffer

    大家好,我是大明哥,今天我们来看看 Buffer. 上面几篇文章详细介绍了 IO 相关的一些基本概念,如阻塞.非阻塞.同步.异步的区别,Reactor 模式.Proactor 模式.以下是这几篇文章的 ...

  8. loto示波器实践——超声波测距模块

    我们这里用到的超声波测距模块,一般是用于arduino智能小车自动避障的.经常见到的应用是使用单片机或者stm32和这种模块结合进行开发的. 我们使用LOTO示波器可以更直观和快速的看到超声波测量距离 ...

  9. Salesforce Consumer Goods Cloud 浅谈篇三之 行动计划(Action Plan)相关配置

    本篇参考: https://v.qq.com/x/page/f0772toebhd.html https://v.qq.com/x/page/e0772tsmtek.html https://v.qq ...

  10. Spark SQL知识点与实战

    Spark SQL概述 1.什么是Spark SQL Spark SQL是Spark用于结构化数据(structured data)处理的Spark模块. 与基本的Spark RDD API不同,Sp ...