非标准的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是一个道理,本质上都是数据段中储存了数据用于判断跳转,只不过语法表由我们自己实现,我们能掌控更多细节,能分开四处使用。
嗯。。其实我就是想让看这里的人看着累。。。感谢我吧。。哈哈。。
未完待续。。。
非标准的xml解析器的C++实现:二、解析器的基本构造:语法表的更多相关文章
- 非标准的xml解析器的C++实现:三、解析器的初步实现
如同我之前的一篇文章说的那样,我没有支持DTD与命名空间, 当前实现出来的解析器,只能与xmlhttp对比,因为chrome浏览器解析大文档有bug,至于其他人实现的,我就不一一测试了,既然都决定自己 ...
- 非标准的xml解析器的C++实现:一、思考基本数据结构的设计
前言: 我在C++项目中使用xml作为本地简易数据管理,到目前为止有5年时间了,从最初的全文搜索标签首尾,直到目前项目中实际运用的类库细致到已经基本符合w3c标准,我一共写过3次解析器,我自己并没有多 ...
- 3.非标准的NDEF格式数据解析--IsoDep
1.使用目的:正常开发是针对NDEF格式数据进行开发,但实际情况并非如此,以厦门公交卡为例,厦门公交卡保存的是非NDEF格式数据.其类型是IsoDep类型. 2.非标准的NDEF格式数据流程:当厦门公 ...
- Python解析非标准JSON(Key值非字符串)
采集数据的时候经常碰到一些JSON数据的Key值不是字符串,这些数据在JavaScript的上下文中是可以解析的,但在Python中,没有该部分数据的上下文,无法采用json.loads(JSON)的 ...
- python之读取配置文件模块configparser(三)高级使用---非标准配置文件解析
非标准配置文件也是经常使用的,如何使用configparser来解析? 这要从configparser本身解析结构来说,configparser包含section和option,非标准配置文件只有op ...
- 《Mybatis 手撸专栏》第9章:细化XML语句构建器,完善静态SQL解析
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你只是在解释过程,而他是在阐述高度! 如果不是长时间的沉淀.积累和储备,我一定也没有 ...
- [python标准库]XML模块
1.什么是XML XML是可扩展标记语言(Extensible Markup Language)的缩写,其中的 标记(markup)是关键部分.您可以创建内容,然后使用限定标记标记它,从而使每个单词. ...
- Android程序解析XML文件的方法及使用PULL解析XML案例
一.一般解析XML文件的方法有SAX和DOM.PULL (1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信 ...
- Wireshark分析非标准端口号流量
Wireshark分析非标准端口号流量 2.2.2 分析非标准端口号流量Wireshark分析非标准端口号流量 应用程序运行使用非标准端口号总是网络分析专家最关注的.关注该应用程序是否有意涉及使用非 ...
随机推荐
- 单片机stm32串口分析
stm32作为现在嵌入式物联网单片机行业中经常要用多的技术,相信大家都有所接触,今天这篇就给大家详细的分析下有关于stm32的出口,还不是很清楚的朋友要注意看看了哦,在最后还会为大家分享有些关于stm ...
- 网络原理数据链路层之差错控制(检错编码和纠错编码)->(奇偶校验码、CRC循环冗余码、海明码)
文章转自:https://blog.csdn.net/weixin_43914604/article/details/104864783 学习课程:<2019王道考研计算机网络> 学习目的 ...
- 微服务(五)nacos配置管理
1 统一配置管理 1.1 nacos中添加配置文件 注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要.基本不会变更的一些配置还是保存在微服务本地比较好. 1.2 从微服务拉取配置 微 ...
- 浅议NetMQ常见模式和消息加密机制
浅议NetMQ常见模式和消息加密机制 概述 在传统企业级开发中,消息队列机制已经成为一种非常常见的技术实现手段,而基于NetMQ则看起来有点像一朵"奇葩",看起来从名字似乎是一个消 ...
- 谷歌chrome多个相同用户登陆同一个机器多开配置
创建快捷方式,目标中填写:路径+参数如下所示即可 参数:--user-data-dir=%LOCALAPPDATA%\Google\Chrome\%SessionName%
- Spark面试题(四)
1.Spark中的HashShufle的有哪些不足? 1)shuffle产生海量的小文件在磁盘上,此时会产生大量耗时的.低效的IO操作: 2)容易导致内存不够用,由于内存需要保存海量的文件操作句柄和临 ...
- kubernetes创建用户
创建k8s User Account 使用openssl方法创建普通用户 准备工作 1 2 3 4 mkdir /root/pki/ 将k8s ca.pem ca-key.pem 证书拷贝到此目录 ...
- vue 快速入门 系列 —— 使用 vue-cli 3 搭建一个项目(上)
其他章节请看: vue 快速入门 系列 使用 vue-cli 3 搭建一个项目(上) 前面我们已经学习了一个成熟的脚手架(vue-cli),笔者希望通过这个脚手架快速搭建系统(或项目).而展开搭建最好 ...
- Effective C++ 总结笔记(二)
二.构造/析构/赋值运算 05.了解C++默默编写并调用那些函数 如果自己不声明, 编译器就会暗自为class创建一个default构造函数.一个copy构造函数.一个copy assignment操 ...
- [cf1349D]Slime and Biscuits
枚举最终的获得所有饼干的人$i$(以下简称"获胜者"),对于$i$获胜的情况,令其贡献为游戏轮数,否则其贡献为0,记$F_{i}$为期望贡献(即所有情况概率*贡献之和),答案即为$ ...