在linux平 台下的较为庞大的命令一般都带有一个配置文件,用于存储该命令启动时要设置的参数,用户还可以变更该配置文件中的某些域的值。因此,在命令中就要考虑怎么 来存取这些文件里的值。一般情况下,大多数程序员都愿意自己编写一段程序来解析配置文件里内容,在配置文件比较小的情况下,该中方法也非常方便适用,我平 时也喜欢这么作。但是,当在配置文件非常大情况下,配置文件里面的section,field非常多的情况下,自己编写程序来读取的话就变得非常麻烦和挠头了。幸亏,linux下还有lex/yacc这么一套工具来帮助程序员来完成这样的工作。
在互联网上,搜索了lex,yacc这两个关键字,可以发现很多的相关文章,但这些文章大都是千篇一律,讲了那么一大堆理论,看完之后还是不知道在C程序里怎么使用lex/yacc。只有这些文章《Lex和Yacc从入门到精通》还比较贴近实际编码,对我有一些帮助,建议大家可以先看看。
在这里,我就从配置文件解析这个日常编程经常要碰到的问题来作为例子,来告诉大家lex/yacc这玩意儿的使用方法。
首先,说明一下大概的原理。先看一下下面的图.
注:本文中的图摘自《》
Figure1: Execution path and data flow in a typical lex/yacc derived parser
图1显示了在一个典型的扫描器/解析器的应用中的控制和数据流程。这个数据流通过扫描器规约成很多符号并且标识成有效的符号组合聚集在解析器中。也就是说,输入流(或者说是配置文件)中的所有单词通过lex工具的扫描生成很多的符号(token)--- 就是资料上常说的词法分析,然后,这些符号再通过yacc工具进行语法解析(按照,编译原理中的巴克斯范式)。
图2描述了一个基于lex/yacc的应用程序的流程。一般情况下,做成一个读取配置文件的程序都要包含两个脚本文件,第一个是lex脚本文件(文件名的后缀一般都是.l形式,不过文件名的形式都没有作特别严格的定义),该脚本文件主要是利用正则表达式的形式对文件进行对配置文件进行正则匹配,这一点非常类似linux的awk工具。另外一个脚本就是yacc脚本(后缀名一般为.y形式),它主要是根据lex的分析结果进行语法解析。
Figure 2: Development sequence when using lex and yacc
图3表示了配置文件解析的数据流。正如下图描述的那样,配置文件里的内容首先是作为字符流的形式,通过lex的yylex函数进行正则匹配后形成许多符号,然后作为yacc的输入,通过yyparse函数对这些符号流进行语法解析。
Figure 4: Data flow in a configuration file parser application
在上面提到的lex脚本,yacc脚本文件作成后,只需要在你自己的C程序中,手动调用yacc的yyparse函数就可以了,只不过,还需要在C程序中添加一些存储yacc解析结果的处理函数,这得根据程序中的实际情况来定。
下面是两个脚本文件和一个C程序文件的样例:lex脚本: test.conf.l
yacc脚本:test.conf.y
c源文件:test_conf.c
test_conf.c第二版
编译方法及执行:
cvs parser #lex test_config.l
cvs parser #yacc -d test_config.y
cvs parser #cc lex.yy.c y.tab.c test_config.c -o test_config
cvs parser # ./test_config test.conf.example