解析器(一):分隔符指导

  又是一个新的系列,那个什么是“解析器”?在我的认知里,大概代表了如下的东西:

  1、格式解析,将固定格式的字符串内容,翻译成我们能够简单获取和处理的结构;如配置文件(.ini)、XML。

  2、编译器+虚拟机,其实就是某一门语言的前端+后端+运行时,当然也可以解释执行;如本人的Xmas,轻量的有正则表达式(Regex)。

  所以,本系列是打算讲述:配置文件(或许有Xml乱入)---正则表达式---Xmas(目前个人实现的最复杂的部分)。所以,接下来,回到本文的主题:

  

  分隔符指导解析:使用固定字符进行语法分割的简单的一种解析方式。

  比如配置文件:

[GC.Mark]
MajorAge = 5
ElevateRatio = 5
MinorBytes = 2097152
MajorBytes = 104857600

[GC.Generation]
MajorAge = 5
SurvivorRatio = 5
MinorBytes = 2097152
MajorBytes = 10485760
FixedBytes = 1048576

  上面就是最典型的配置文件的一部分。其中只有4个我们需要注意的【分隔字符】:

  1、【[】,代表了一个条目的名字的开始

  2、【]】,一个条目名字的结束,其后所有的内容都是该条目的键值对,直到遇到一个新的条目

  3、【=】,键值对的分隔符,其前面的是键(key),后面的则是值(value)

  4、【;】,注释

  当然还有一个隐式的分隔符:【\n】换行符,所有的内容都以它作为结束;这一点至关重要:解析工作,可以以行为单位。

std::string context;
Files.read("config.ini", context);
std::vector<std::string> lines;
Strings.split(context.c_str(), ‘\n‘, lines);
for(auto& line, lines){
    parseLine(line);
}

  这样,我们的工作对象,就将整个字符串减少到一行。

  在这里,我们可以预见:解析配置文件,是简单而枯燥无味的;是的,的确如此:

void parseLine(const std::string& line)
{
    if(line == ""){
        return;
    }
    if(line[0] == ‘[‘){
        std::string item(&line[1], &line[line.length() - 1]);
        setCurrentItem(item);//设置当前所处的条目环境
        return;
    }
    if(line[0] == ‘;‘){
        return;
    }
    int offset = line.find(‘=‘);
    if(offset < 0){
        return;
    }
    std::string key(&line[0], &line[offset]);
    std::string value(&line[offset + 1];
    addValue(key, value);
}

  如此简单地我们就完成了解析任务的最核心的部分;可惜的是,丝毫没有任何难度和挑战。当然,我们可以做一些优化工作:

  1、考虑【]】的匹配,前面我们并没有考虑它;只是默认,条目所在行的最后字符一定就是【]】;当然,这样也才是正确的配置格式;但是,我们可以容错,至于需要容许怎样的错误,由自己喜好。

  2、去空隔,特别是键值对,其中【=】的前后可能会有空格;我们有必要去掉它。

  3、更多的注释支持,如:不止使用【;】、其位置可以不是每行的开始。

  下一个问题,就是:这些解析出的信息,放在哪里?

  作为一个没有难度和特色的ini解析器,我们可以这样:

void setCurrentItem(const std::string& item)
{
    mItem = item;
}

void addValue(const std::string& key, const std::string& value)
{
    mValues[mItem + "." + key] = value;//注意这个“.”的作用
}

  我们使用一个Map来存放键值对,而配置文件只有键值对。到此,我们基本上完成了所有的工作;只剩对外的查询接口了。

  我们支持怎样的查询?

  来点有意思的如何:

    String    value(const String& name)const;
    ItemList  items(const String& name)const;
    ValueList values(const String& name)const;

  我个人的解析器,支持层级查询:类似于文件目录结构,一个层级内部有多个下一级层级和多个值(对应一个文件夹内部有多个文件夹和多个文件)。要仔细分析这句话:

  1、有目录结构,即:一个条目长这样“GC.Mark”,意味着其有两级对应的层次“GC”和它的下一级“Mark”(使用“.”作为层分隔)

  2、有多个层级,即:我们的配置文件,其实是一棵树(在当前定义下)

  3、当前层级有多个值,而非键值对。

  这样,我们将支持这样的格式:

[Xmas.Source]
H:\Xmas\Xmas.xmas
H:\Xmas\Tools.xmas

  一个列表。当我使用如下代码时:

sourceList = config.values("Xmas.Source");

  它将返回一个字符串列表 ["H:\Xmas\Xmas.xmas",  "H:\Xmas\Tools.xmas"]。

  当我使用如下代码时:

itemList = config.items("GC");

  它将返回两个条目:“Mark”、“Generation”。然后,沿着条目,我可以继续向下遍历:

class Item{
public:
    String name()const;
    String value()const;
    String value(const String& name)const;
public:
    ItemList  items()const;
    ValueList values()const;
};

  通过该条目,我便可以像访问一颗树一样。当然,这时该解析器内部正是构建了一个真正的DOM树。

  聪明的读者应该发现我早已偏离了本文的主题:分隔符指导解析。是的,使用分隔符解析,是一种简单且连入门都算不上的解析方式;但其作为一个指引,指向更完善的语法指导,还是有价值的。

  在语法指导解析中,充满了各种树形结构;所以,我们必须了解、知道、精通,一棵树的创建和访问。而前面带有层级的配置解析器,是一个不错的开始。

时间: 2024-08-24 19:35:37

解析器(一):分隔符指导的相关文章

如何实现一个Java Class 解析器

原文出处: tinylcy 最近在写一个私人项目,名字叫做ClassAnalyzer,ClassAnalyzer的目的是能让我们对Java Class文件的设计与结构能够有一个深入的理解.主体框架与基本功能已经完成,还有一些细节功能日后再增加.实际上JDK已经提供了命令行工具javap来反编译Class文件,但本篇文章将阐明我实现解析器的思路. Class文件 作为类或者接口信息的载体,每个Class文件都完整的定义了一个类.为了使Java程序可以"编写一次,处处运行",Java虚拟机

使用Scala基于词法单元的解析器定制EBNF范式文法解析

一.前言 近期在做Oracle迁移到Spark平台的项目上遇到了一些平台公式翻译为SparkSQL(on Hive)的需求,而Spark采用亲妈语言Scala进行开发.分析过大概需求过后,拟使用编译原理中的EBNF范式模式,进行基于词法的文法解析.于是拟采用传统的正则词法解析到EBNF文法解析的套路来实现,直到发现了StandardTokenParsers这个Scala基于词法单元的解析器类. 二.平台公式及翻译后的SparkSQL 平台公式的样子如下所示: 1 if(XX1_m001[D003

optparse 模块—— 命令行选项的解析器

15.5 optparse 模块--  命令行选项的解析器 注意:从2.7版本后不再使用:optparse模块不推荐使用,python不再更新该模块,后续的发展将推荐使用argparse模块. 支持python2.3及以上版本 optparse模块比旧的getopt模块具有更方便.更灵活.功能更强大的解析命令行选项的库.optparse使用一种更加声明式的命令行解析风格:你创建一个OptionParser实例,填充选项,并解析命令行.optparse允许用户指定选项,使用传统的GNU/POSIX

CSV文件格式解析器的实现:从字符串Split到FSM

本文分为5小节,基本上就是我刚接触CSV文件到思考.实践做一个CSV解析器的过程的还原.希望我的思路也能带领你一步步从浅到深认识CSV文件格式. 1.简单的CSV解析器实现. 2.简单实现的CSV解析器的问题 3. CSV格式的定义 4.用FSM(有限状态机)来做CSV格式解析. 5.为什么使用CSV格式 1.简单的CSV解析器实现. 最近有一个需求,读取CSV格式的配置.CSV是CommaSeparated Value(逗号分隔值)的缩写,通常用文本表示数据.CSV格式数据的结构类似表格,不同

MySQL内核源码解读-SQL解析之解析器浅析

MYSQL服务器接收SQL格式的查询,首先要对sql进行解析,内部将文本格式转换为二进制结构,这个转换就是解析器,解析的目的是为了让优化器更好的处理指令,以便以最优的路径,最少的耗时返回我们想要的结果.sql解析器的构成:1.词法分析(Lexical scanner):作用是将整个查询分解为多个元素.2.语法规则(Grammar rule module):寻找sql语法规则组合,产生一个序列,执行这些规则相关的代码.1 and 2 产生一棵解析树,提供给优化器使用.mysql解析器的特殊性在于它

Python之父新发文,将替换现有解析器

花下猫语: Guido van Rossum 是 Python 的创造者,虽然他现在放弃了"终身仁慈独裁者"的职位,但却成为了指导委员会的五位成员之一,其一举一动依然备受瞩目.近日,他开通了 Medium 账号,并发表了第一篇文章,透露出要替换 Python 的核心部件(解析器)的想法.这篇文章分析了当前的 pgen 解析器的诸多缺陷,并介绍了 PEG 解析器的优点,令人振奋.这项改造工作仍在进行中,Guido 说他还会写更多相关的文章,我们就拭目以待吧. 本文原创并首发于公众号[Py

[Python]HTML/XML解析器Beautiful Soup

[简介] Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库.即HTML/XMLX的解析器. 它可以很好的处理不规范标记并生成剖析树(parse tree). 它提供简单又常用的导航(navigating),搜索以及修改剖析树的操作.它可以大大节省你的编程时间. [安装] 下载地址:点击打开链接 Linux平台安装: 如果你用的是新版的Debain或ubuntu,那么可以通过系统的软件包管理来安装: $ apt-get install Python-bs4 B

lua标签解析器

lua 标签解析器 概述 一个类xml标签解析函数,将标签解析成Lua中的表结构它可以用来解析简单xml结构,可以作为RichLabel控件的字符串解析组件(其实它现在就是这么用的;-)) 原理 使用lua的模式匹配, 使用了模式串%b<>%b用来匹配对称的字符.常写为 %bxy,x和y是任意两个不同的字符. x作为匹配的开始,y作为匹配的结束.比如,%b<> 匹配以 < 开始,以 > 结束的字符串 要解析的字符串 hello world <div>hell

springmvc 前段控制器 处理器映射器 处理器适配器 视图视图解析器 配置

1. 前段控制器 新建项目在web.xml中配置前段控制器 <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</pa