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

引擎

区别点

DFA
Deterministic finite automaton
确定型有穷自动机

DFA引擎它们不要求回溯(并因此它们永远不测试相同的字符两次),所以匹配速度快!DFA引擎还可以匹配最长的可能的字符串。

不过DFA引擎只包含有限的状态,所以它不能匹配具有反向引用的模式,还不可以捕获子表达式

代表性有:awk,egrep,flex,lex,MySQL,Procmail

NFA
Non-deterministic finite automaton

非确定型有穷自动机

又分为传统NFA,Posix NFA

传统的NFA引擎运行所谓的“贪婪的”匹配回溯算法(longest-leftmost),

以指定顺序测试正则表达式的所有可能的扩展并接受第一个匹配项。

传统的NFA回溯可以访问完全相同的状态多次,在最坏情况下,它的执行速度可能非常慢,但它支持子匹配

代表性有:GNU Emacs,Java,ergp,less,more,.NET语言

,PCRE
library,Perl,PHP,Python,Ruby,sed,vi
等,

一般高级语言都采用该模式。

DFA以字符串字符为主,逐个在正则表达式匹配查找,而NFA以正则表达式为主,在字符串中逐一查找。尽管速度慢,但是对操作者来说更简单,因此应用更广泛!下面所有以NFA引擎举例说明,解析过程!

解析引擎眼中的字符串组成
对于字符串“DEF”而言,包括D、E、F三个字符和 0、1、2、3 四个数字位置(零宽空间):0D1E2F3,对于正则表达式而言所有源字符串,都有字符和位置。正则表达式会从0号位置(可以匹配^),逐个去匹配的。

占有字符和零宽度
正则表达式匹配过程中,如果子表达式匹配到的是字符内容,而非位置,并被保存到最终的匹配结果中,那么就认为这个子表达式是占有字符的(IsMatch开始为true);如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的(IsMatch不为true)。占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。常见零宽字符有:^,(?=)等

正则表达式匹配过程详解实例
我们掌握了上面几个概念,我们接下来分析下几个常见的解析过程。结合使用软件regexBuddy来分析。

regexbuddy正则表达式测试工具使用方法(图文)

1、安装完regexbuddy

该工具支持多种程序语言正则表达式,如:perl,pcre,javascript,python,ruby,c#,java等等,还能自动生成程序代码,并且内部带有大量的常用正则表达式。

2、一般切换到side by side:

3、匹配过程

匹配完,点击“Test”里面Debug(Here),自动切换到Debug界面:

匹配过程:\w+一下子贪婪匹配aaa27111ab,然后\d+没有匹配字符串了。开始回逆了,逐个字符减少,直到发现最后一个字符“1”与\d+匹配为止。最终匹配到字符串是:“aaa27111”

从上面一个匹配看,这个简单一个匹配,搜索了8次,进行了不断查找。如果我们已经准确知道自己要匹配什么样字符,我们可以对源正则表达式修改下,减少匹配次数。就达到优化正则表达式目的,提高匹配效率!

如果我们知道源字符串只是a-z字符,进行修改发现,只要用2次搜索就匹配到所需字符。

为什么需要性能测试工具 
我们都知道,正则表达式使用进行搜索查找,没有字符串直接查找快!而且性能是几何倍数下降。那么,为什么正则表达式速度会比字符串搜索慢呢。我们来看看,正则表达式查找字符串的匹配过程吧。正则表达式由一些元字符,普通字符,量词字符组合成。默认情况下,这些量词元字符(*,+,?)都是贪婪模式,会最大长度匹配字符串。我们知道,正则表达式往往搜索路径会有多个,我们看看,下面匹配过程。就知道,主要影响正则表达式执行性能有哪些了。

正则表达式匹配过程如:\d+abc,元字符是:”12345bdc”,查找会从左向右进行,\d+,贪婪模式,一下子匹配到12345,然后bdc与\d+不能匹配,”abc”中,”a”字符,开始匹配”bdc”,发现匹配失败。正则表达式开始回溯匹配(贪婪模式量词开始逐一减少匹配字符长度),\d+只匹配”1234”,”5bdc”与”abc”匹配,任然失败。\d+继续减少匹配长度为:”123”,”45bdc”与”abc”匹配,任然失败。继续回退,直到\d+匹配”1”,用”2345bdc”与”bdc”匹配,任然失败。整个匹配就失败了。

从上面过程中,我们发现,每次回溯,要重新操作匹配因此匹配搜索次数,直接影响正则表达式的性能。做正则表达式性能优化,一般就是优化查询的次数。这个是我们分析过程,如果有个工具能够实实在在看到每一步匹配过程,对于我们优化正则表达式将带来太多方便了。这里介绍工具是:regexbuddy软件,它就是一个实实在在看到匹配过程工具。

Demo1: 源字符DEF,对应标记是:0D1E2F3,匹配正则表达式是:“DEF”

过程可以理解为:首先由正则表达式字符 “D” 取得控制权,从位置0开始匹配,由 “D” 来匹配“D”,匹配成功,控制权交给字符 “E” ;由于“D”已被 “D” 匹配,所以 “E” 从位置1开始尝试匹配,由“E” 来匹配“E”,匹配成功,控制权交给 “F”;由“F”来匹配“F”,匹配成功。

Demo2:源字符DEF,对应标记是:0D1E2F3,匹配正则表达式是:/D\w+F/

过程可以理解为:首先由正则表达式字符 /D/ 取得控制权,从位置0开始匹配,由 /D/ 来匹配“D”,匹配成功,控制权交给字符 /\w+/ ;由于“D”已被 /D/ 匹配,所以 /\w+/ 从位置1开始尝试匹配,\w+贪婪模式,会记录一个备选状态,默认会匹配最长字符,直接匹配到EF,并且匹配成功,当前位置3了。并且把控制权交给 /F/ ;由 /F/ 匹配失败,\w+匹配会回溯一位,当前位置变成2。并把控制权交个/F/,由/F/匹配字符F成功。因此\w+这里匹配E字符,匹配完成!

Demo3:源字符DEF,对应标记是:0D1E2F3,匹配正则表达式是:/^(?=D)[D-F]+$/

过程可以理解为:元字符 /^/ 和 /$/ 匹配的只是位置,顺序环视(匹配完开头,从左往右依次匹配) /(?=D)/ (匹配当前位置,右边是否有字符“D”字符出现)只进行匹配,并不占有字符,也不将匹配的内容保存到最终的匹配结果,所以都是零宽度的。 首先由元字符 /^/ 取得控制权,从位置0开始匹配, /^/ 匹配的就是开始位置“位置0”,匹配成功,控制权交给顺序环视 /(?=D)/;/(?=D])/ 要求它所在位置右侧必须是字母”D”才能匹配成功,零宽度的子表达式之间是不互斥的,即同一个位置可以同时由多个零宽度子表达式匹配,所以它也是从位置0尝试进行匹配,位置0的右侧是字符“D”,符合要求,匹配成功,控制权交给 /[D-F]+/ ;因为 /(?=D)/ 只进行匹配,并不将匹配到的内容保存到最后结果,并且 /(?=D)/ 匹配成功的位置是位置0,所以 /[D-F]+/ 也是从位置0开始尝试匹配的, /[D-F]+/ 首先尝试匹配“D”,匹配成功,继续尝试匹配,直到匹配完”EF”,这时已经匹配到位置3,位置3的右侧已没有字符,这时会把控制权交给 /$/,元字符 /$/ 从位置3开始尝试匹配,它匹配的是结束位置,也就是“位置3”,匹配成功。此时正则表达式匹配完成,报告匹配成功。匹配结果为“DEF”,开始位置为0,结束位置为3。其中 /^/ 匹配位置0, /(?=D)/ 匹配位置0, /[D-F]+/ 匹配字符串“DEF”, /$/ 匹配位置3。

匹配详解

  1. 用“(\$)”匹配“$1.30”

匹配结果:

1.1.尝试从“ $ 1 . 3 0 ”的第一个“零宽空间”开始匹配“(\$)”: “ $ 1 . 3 0 ”匹配到,IsMatch=true。

1.2.   尝试从“ $ 1 . 3 0 ”的第二个“零宽空间”开始匹配“(\$)”:“1 . 3 0 ”依次均不匹配。

2. 用“(\$*)”匹配“$1.30”

匹配结果:

2.1.尝试从“ $ 1 . 3 0 ”的第一个“零宽位”开始匹配“(\$*)”: “ $ 1 . 3 0 ”匹配。(\$*是尽可能多的匹配$,此处匹配了1次$)

2.2尝试从“ $ 1 . 3 0 ”的第二个“零宽位”开始匹配“(\$*)”:由于1符合\$*(\$*是尽可能多的匹配$,此处匹配了0次$),所以“ 1”中的零宽空间被捕获,但1未被捕获。

2.3尝试从“ $ 1 . 3 0 ”的第三个“零宽位”开始匹配“(\$*)”:由于.符合\$*(\$*是尽可能多的匹配$,此处匹配了0次$),所以“ .”中的零宽空间被捕获,但.未被捕获。

2.4尝试从“ $ 1 . 3 0 ”的第四个“零宽位”开始匹配“(\$*)”:由于3符合\$*(\$*是尽可能多的匹配$,此处匹配了0次$),所以“ 3”中的零宽空间被捕获,但3未被捕获。

2.5尝试从“ $ 1 . 3 0 ”的第五个“零宽位”开始匹配“(\$*)”:由于0符合\$*(\$*是尽可能多的匹配$,此处匹配了0次$),所以“ 0”中的零宽空间被捕获,但0未被捕获。

2.6尝试从“ $ 1 . 3 0 ”的第六个“零宽位”开始匹配“(\$*)”:由于$(结尾)符合\$*(\$*是尽可能多的匹配$,此处匹配了0次$),所以“ $”(结尾)中的零宽空间被捕获,但结尾未被捕获。

3. 用“((1))”匹配“$1”

匹配结果:

[No0000100]正则表达式匹配解析过程分析(正则表达式匹配原理)&regexbuddy使用&正则优化的更多相关文章

  1. Django url配置 正则表达式详解 分组命名匹配 命名URL 别名 和URL反向解析 命名空间模式

    Django基础二之URL路由系统 本节目录 一 URL配置 二 正则表达式详解 三 分组命名匹配 四 命名URL(别名)和URL反向解析 五 命名空间模式 一 URL配置 Django 1.11版本 ...

  2. javascript 正则表达式之分组与前瞻匹配详解

    本文主要讲解javascript 的正则表达式中的分组匹配与前瞻匹配的,需要对正则的有基本认识,本人一直对两种匹配模棱不清.所以在这里总结一下,如有不对,还望大神指点. 1.分组匹配: 1.1捕获性分 ...

  3. 正确匹配URL的正则表达式

    网上流传着多种匹配URL的正则表达式版本,但我经过试验,最好用的还是从stackoverflow上查到的: (https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_| ...

  4. java 正则匹配空格字符串 正则表达式截取字符串

    java 正则匹配空格字符串 正则表达式截取字符串 需求:从一堆sql中取出某些特定字符串: 比如配置的sql语句为:"company_code = @cc and project_id = ...

  5. Delphi 正则表达式语法(6): 贪婪匹配与非贪婪匹配

    Delphi 正则表达式语法(6): 贪婪匹配与非贪婪匹配 //贪婪匹配 var   reg: TPerlRegEx; begin   reg := TPerlRegEx.Create(nil);   ...

  6. java中匹配中文的正则表达式

    java中要匹配中文的正则表达式可以有两种写法:一是使用unicode中文码:二是直接使用汉字字符: 例: (1)String str = "晴"; String regexStr ...

  7. js进阶正则表达式10-分组-多行匹配-正则对象的属性(小括号作用:分组,将小括号里面的东西看成一个整体,因为量词只对前一个字符有效)(多行匹配:m)(属性使用:reg.global)

    js进阶正则表达式10-分组-多行匹配-正则对象的属性(小括号作用:分组,将小括号里面的东西看成一个整体,因为量词只对前一个字符有效)(多行匹配:m)(属性使用:reg.global) 一.总结 1. ...

  8. 廖雪峰Java9正则表达式-2正则表达式进阶-5非贪婪匹配

    1.贪婪匹配 问题:给定一个字符串表示的数字,判断该数字末尾0的个数? "123000": 3个0 "10100": 2个0 "1001": ...

  9. VIM 用正则表达式,非贪婪匹配,匹配竖杠,竖线, 匹配中文,中文正则,倒数第二列, 匹配任意一个字符 :

    VIM 用正则表达式 批量替换文本,多行删除,复制,移动 在VIM中 用正则表达式 批量替换文本,多行删除,复制,移动 :n1,n2 m n3     移动n1-n2行(包括n1,n2)到n3行之下: ...

随机推荐

  1. Wifiner for Mac(WiFi 状况分析工具)破解版安装

    1.软件简介    Wifiner 是 macOS 系统上一款 Wifi 分析工具,仅需几次点击即可对您的 Wi-Fi 网络连接进行分析和故障排除.扫描您的 Wi-Fi 网络,获取包含交互式彩色编码热 ...

  2. react-无状态组件

    import React, { Component } from "react"; //import PostItem from "./PostItem"; / ...

  3. Linux好用的工具命令 - nl/du

    nl 添加行号后打印输出文本内容,以下例子演示了cat 和nl 输出nlDemo文档的区别. [root@ptarmiganantelope:~]# cat nlDemo root:x:0:0:roo ...

  4. java 生成Http 头部date格式的string-RFC 1123 Date Representation in java

    https://blog.csdn.net/lvzhuyiyi/article/details/51770148 ******************************************* ...

  5. 微信小程序测试指南

    [本文出自天外归云的博客园] 微信小程序本地部署测试方法 下载微信开发者工具 让小程序管理员将测试人员的微信号添加开发者权限 本地设置hosts为测试环境hosts 打开微信web开发者工具并扫码登录 ...

  6. Spring-boot(二)yml文件的使用

    上一章创建了一个简单的springboot项目,配置可以说非常的简单. 不过,在实际开发中不可能都用默认的配置,还是需要根据自己的实际项目需求有自定义的配置的. 比如:端口号需要变更,模板引擎的缓存开 ...

  7. Java 分布式和集中式理解

    文章转载自:https://blog.csdn.net/youanyyou/article/details/79406507

  8. 【iCore1S 双核心板_ARM】例程六:WWDG看门狗实验——复位ARM

    实验原理: STM32内部包含窗口看门狗,通过看门狗可以监控程序运行,程序错误 时,未在规定时间喂狗,自动复位ARM.本实验通过按键按下,停止喂狗, 制造程序运行 错误,从而产生复位 . 实验现象: ...

  9. Linux+树莓派3开发总结——树莓派远程文件共享winows

    http://blog.csdn.net/xqf1528399071/article/details/52192134 ———————————————————————————————————————— ...

  10. SmileyCount.java笑脸加法程序代写(QQ:928900200)

    SmileyCount.java 1/4Java Programming 2014Course Code: EBU4201Mini ProjectTask 1 [30 marks]SmileyCoun ...