flex是一个词法分析器生成器,它是编译器和解释器编程人员的常用工具之一。flex的程序主要由一系列带有指令(称为动作代码)的正则表达式组成。在匹配输入时,flex会将所有的正则表达式翻译成确定性有穷自动机,这使得flex等词法分析器生成器生成的词法分析器匹配输入模式的效率非常高。当然,有人指责flex不够灵活,功能有限,很多问题都无法解决,比如Javascript、C++等语言中二义性的问题,实际上很多程序(比如Python的解释器)的词法分析器都是用的手工代码而不是flex自动生成的。这些都不在这篇文章的讨论范围内,我主要想通过对flex和手工写的C代码编译的一个词数统计程序(word counter)来非常粗略地评价一下flex匹配正则表达式的速度如何。

  测试方法:分别运行用flex生的C代码和手工C代码编译的程序,设置三种不同大小的文件,用shell的time指令来测试两个程序运行所用的时间。

  下面上源码,先是flex源码 flexwc.l:

 /*用flex实现的一个词数统计程序。
flexwc.l
*/ %{
/*定义全局变量,分别统计字符数,单词数和行数*/
int chars=;
int words=;
int lines=;
%}
%%
[a-zA-Z]+ { ++words; chars+=strlen(yytext);}/*正则表达式匹配任意单词,用字符串函数统计输入流中的当前字符数*/
\n { ++chars; ++lines; }/*遇到换行符,新的一行开始*/
. {++chars;}/*其它字符*/
%% main(int argc,char **argv)
{
yylex();/*调用flex生成的例程*/
printf("%8d%8d%8d\n",lines,words,chars);/*打印行号,单词数,字符数*/
}

  编译指令:

 $flex flexwc.l
$gcc -o flexwc -O3 flexwc

  下面是C代码,manwc.c:

 /*手工C代码的词数统计程序
manwc.c
*/ #include <stdio.h>
#include <ctype.h>/*使用isalpha()*/ /*全局变量,分别记录文件的字符数,行数和单词数*/
int i_char=,i_line=,i_word=; int main(void)
{
int inword=;
/*当inword==1时说明正在处理一个单词的内部(也就是一个单词还没结束)
否则意味一个单词的结束 */
char ch;
while ((ch=getchar())!=EOF)/*文件未结束*/
{
++i_char;/*增加字符数*/
if ('\n'==ch)/*行数*/
++i_line;
if (isalpha(ch) && !inword)/*一个单词的开始*/
{
inword=;
++i_word;
}
if (!isalpha(ch) && inword)/*一个单词的结束*/
{
inword=;
}
}
printf("%8d%8d%8d\n",i_line,i_word,i_char);/*打印文件行数、单词数、字符数*/
return ;
}

  编译指令:

 $gcc -o manwc -O3 manwc.c

  下面进行第一次测试,指令及结果如下:

 $time ./autowc < foo.txt

 real    0m0.014s
user 0m0.000s
sys 0m0.000s $ time ./manwc < foo.txt real 0m0.024s
user 0m0.000s
sys 0m0.000s

注意,因为flex的默认输入流和C代码的输入流都是stdin,所以这里用到了输入流重定向'<'。

  下面进行第二次测试,指令及结果如下:

 $time ./autowc < lex.yy.c 

 real    0m0.008s
user 0m0.004s
sys 0m0.000s $ time ./manwc < lex.yy.c real 0m0.008s
user 0m0.004s
sys 0m0.000s

  下面进行第三次测试,指令及结果如下:

 $time ./autowc < MAINTAINERS 

 real    0m0.013s
user 0m0.012s
sys 0m0.000s $ time ./manwc < MAINTAINERS real 0m0.019s
user 0m0.020s
sys 0m0.000s

  (此结果受机器影响较大,仅作为参考)

  经过三次规模从小到大的测试,可以看到用flex生成的词法分析器匹配正则表达式的速度几乎总是比手工C代码要快。可以预见,当模式变得更为复杂时,flex生成的代码的执行速度将会比纯手工C代码的效率高更多。这是由于flex处理正则表达式的内部格式(也就是确定性有穷自动机)使得匹配正则表达式时几乎与问题规模无关(也就是说并不是模式越复杂匹配的时间就会越久,但是也会有例外),而用手工的C代码处理此类问题时,总是倾向于将字符流一步步的进行分析,比如分析C语言中的'/'符号,当读入第一个'/'时,并不能确定到底是什么(有二义性),只有继续读接下来的一个字符:如果是一个数字,那'/'就是除法运算符;如果是‘/’那就是一个行注释,可以直接忽略本行余下的内容了;如果是'*'那还要判断什么位置出现了"*/",然后忽略中间的注释,如果找不到匹配的"*/",就需要报错。flex允许你对这三种方式定义明确的正则表达式:"/",“//”,“/*”(最后一种匹配/**/注释的情况还要用到起始状态的知识,这里就不介绍了)。总之记住一点:一次匹配一大段几乎总是比每次匹配一个字符、匹配多次要快,当然也有例外。

  总结:flex作为一种格式化文件处理工具,用它生成的代码不仅可以用于编译器解释器的编程人员的词法分析器开发,还可用于所有需要进行分析的格式化文件,比如快速查找文件中的某一特定格式、自动排版、代码自动缩进、语法着色等等,一切就看你能做什么了!

Flex的正则表达式匹配速度与手工代码的比较的更多相关文章

  1. 使用正则表达式匹配JS函数代码

    使用正则表达式匹配JS函数代码 String someFunction="init"; Pattern regex = Pattern.compile("function ...

  2. [No0000100]正则表达式匹配解析过程分析(正则表达式匹配原理)&regexbuddy使用&正则优化

    常见正则表达式引擎引擎决定了正则表达式匹配方法及内部搜索过程,了解它至关重要的.目前主要流行引擎有:DFA,NFA两种引擎. 引擎 区别点 DFA Deterministic finite autom ...

  3. 正则表达式匹配${key}并在Java中使用

    1.正则表达式匹配${key} \$\{([a-z]+)\} 能够匹配字符串中以${key}形式的文本(其中key为小写应为字母) .*\$\{([a-z]+)\}.* 可以用来检测文本中是否有${k ...

  4. [LeetCode] Regular Expression Matching 正则表达式匹配

    Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...

  5. 正则表达式匹配/data/misc/wifi/wpa_supplicant.conf的WiFi名称与密码

    正则表达式匹配/data/misc/wifi/wpa_supplicant.conf的WiFi名称与密码: String regex_name="ssid=\"(.*?)\&quo ...

  6. 在Visual Studio中使用正则表达式匹配换行和批量替换

    系统环境:Windows 8.1 Enterprise Update 2 x64 开发环境:Mircosoft Visual Studio Ultimate 2013 Update 2 RC 问题:如 ...

  7. .NET正则表达式匹配Silverlight

    这是一个.NET正则表达式匹配工具的Silverlight 在页面中加入以下代码就可以了: <"> <param name="source" value ...

  8. c# 正则表达式 匹配中括号&颜色过滤

    现在需要匹配 [color=#000000],以"[color"开头,以"[/color]"结束,中间字符数量不限制,最后返回所有匹配的下标. 代码如下: // ...

  9. *****正则表达式匹配URL

    最近将匹配URL的正则替换了下 之前的是: ((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3 ...

随机推荐

  1. tomcat支持多少并发

    作者:孟男男 来源:https://zhidao.baidu.com/question/1445941399668603020.html Tomcat的最大并发数是可以配置的,实际运用中,最大并发数与 ...

  2. Ubuntu升级内核

    看到微软开源.兼容.豁达,很高兴,今天研究一下Docker,查看Ubuntu内核版本,发现2.3有点低,不符合当前版本. 最低内核版本要求3.10. 升级Ubuntu内核. 首先 下载内核 网站htt ...

  3. hdu 3064 twoNumber

    twoNumber Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 2048/1024 K (Java/Others) Total Su ...

  4. JasperReports教程:Report Data Sources

    原文地址:http://www.tutorialspoint.com/jasper_reports/jasper_report_data_sources.htm Datasources是一个结构化的数 ...

  5. Hive UDF’S addMonths

    our project use hive 0.10 , and in the hiveql , we need use addMonths function builtin in hive-0.11. ...

  6. 探索 OpenStack 之(9):深入块存储服务Cinder (功能篇)

    继研究了Neutron之后,继续Nova的外围研究之旅.本站是研究块存储服务Cinder. 0.验证环境 环境包括: 1.一个controller节点,运行nova-api, nova-schedul ...

  7. 原创翻译-测试驱动开发(TDD)

    测试驱动开发原则 翻译自<<Expert Python Programming>> 测试驱动开发是指首先编写包含所有测试软件特点的测试集,然后再去开发软件.也就是说,在编写软件 ...

  8. 为Unity项目生成文档(一)

    VS生成chm帮助文档 VS代码中使用Xml注释,并通过Sandcastle生成chm文档的文章,这几篇值得分享: 使用.NET中的XML注释(一) -- XML注释标签讲解 使用.NET中的XML注 ...

  9. Jenkins学习一:Jenkins是什么?

    文章转载:http://www.cnblogs.com/zz0412/tag/jenkins/default.html?page=1 第一章 Jenkins是什么? Jenkins 是一个可扩展的持续 ...

  10. PCTF-2016-WEB

    Pctf ** web100 PORT51**  开始看到这个真的无法下手,想过用python–socket编程或者scapy发包.自己觉得是可以的,但是没有去试,后面看一大神writeup,知道: ...