正则表达式 Regular Expression
发布时间:2020-05-22 20:19:06 所属栏目:程序设计 来源:互联网
导读:http://www.cnblogs.com/lovewife/articles/1438417.html 字符的表示 1. 普通字符,特殊字符: 特殊字符:.|*?+(){}[]^$,相当于语言的关键字,这些字符前面加转义符表示字符本身,否则就作为正则表达式特殊用途字符。 特殊转义字符:下表主要针对.Net的正则
|
http://www.cnblogs.com/lovewife/articles/1438417.html 字符的表示 2. 字符枚举:中括号括起来,例如[abc]表示出现a,b,c中任意一个字符都可以;[^abc]则匹配除了a,c之外的任意一个字符。 3. 字符范围:[a-zA-Z0-9]。 4. 综合表示: Alternation,Condition Constructs(等价、条件式结构) 1. Alternation Construct: (pattern1)|(pattern2),子表达式1或者2任意一个匹配就匹配成功。 2. Condition Construct: (?(expression)(patternYes)|(patternNo)),如果符合expression,则使用patternYes子表达式进行匹配,否则使用patternNo进行匹配。expression也可以是backreference中的group名字,backreference后面详细讲述。这个语法形式是.Net正则表达式的,其它正则表达式引擎也有支持条件式结构的,但语法可能不一样。 例如正则式(?(d{4})((19|20)d{2})|(d{2}))匹配文本串"2004年 98年",结果是"2004","98",其中"2004"是使用patternYes部分匹配出来的,而"98"是使用patternNo部分。 Quantifier(量词) 指表达式需要重复匹配多少次。 * 0或多次 + 1或多次 ? 作为量词时表示0或1次,在其它量词后面出现时作为greedy,lazy/non-greedy模式开关。 {n} n次 {n,} 最少n次 {n,m} 最少n次,最多m次 *?,+?,{n}?,{n,}?,m}? 开启lazy/non-greedy模式,例如{n,m}表示最少n次,最多m次,在这个范围内尽可能少的匹配。 greedy,lazy/non-greedy模式参考下面相关部分。 Zero-Width Assertions(零宽度断言) 这种结构产生的匹配结果长度为0(所以称作零宽度),只是用于对上下文环境做判断(所以称作断言)。 ^ 如果使用Multiline选项,匹配每一行的开始位置;如果使用Singleline选项,匹配整个字符串开始位置。^如果出现在[]中就不是Zero-Width Assertion了。 $ 如果使用Multiline选项,匹配每一行的结束位置(n之前);如果使用Singleline选项,匹配整个字符串结束位置。 A 忽略Multiline选项(相当于取消Multiline选项并设置Singleline选项),匹配整个字符串开始位置。 Z 忽略Multiline选项,匹配整个字符串结束位置,中间的n不会考虑。.Net中z和Z的效果完全一样,不知道是bug还是怎么回事。 前面提到的条件式结构,以及b如果不是出现在[]中,都是Zero-Width Assertions;后面将会讲到的lookahead,lookbehind也是一种Zero-Width Assertion。 Multiline,Singleline等,参考正则表达式选项部分。 Greedy,Lazy/Non-greedy(贪婪模式,惰性模式) 在使用quantifier量词修饰,需要执行重复n次的匹配中,greedy模式尽可能多的匹配更多字符,而lazy/non-greedy模式则尽可能少的匹配字符。 NFA(参考后面NFA,DFA部分)默认都是采用greedy模式,而当今主要正则表达式引擎都是NFA,因此注意greedy模式的影响。将greedy模式改为non-greedy,在量词修饰符后面添加"?"实现。例如NFA中w+为greedy模式,w+?为non-greedy模式。 文本串为:"dxxxdxxxd",greedy模式: 文本串为:"dxxxdxxxd",non-greedy模式: Group,Back Reference(分组、反向引用) 正则表达式引擎不仅记录最终的匹配结果,使用()括起来的子表达式匹配到的文本串,在匹配过程中也会记录下来。在.Net中,可以使用Match.Groups访问某个匹配结果的所有分组。 没有指定名称的分组称为匿名分组,对所有的匿名分组都默认有一个组号。.Net中组号为0的分组是整个正则表达式,而不管这个正则表达式是否使用()括起来了。对于其它的匿名分组,根据左括号出现的先后位置依次从1开始编号。例如表达式((abc)d+)?(xyz)(.*)总共有5个分组,依次为0:((abc)d+)?(xyz)(.*),1:((abc)d+),2:(abc),3:(xyz),4:(.*)。 可以对分组命名,.Net中命名方法:(?<groupName>patterns)。如果存在命名分组,则所有命名分组的编号将从最后一个匿名分组的位置开始,依次递增。例如表达式((?<group1>abc)d+)?(?<group2>xyz)(.*)的5个分组依次为0:((?<group1>abc)d+)?(?<group2>xyz)(.*),1:((?<group1>abc)d+),2:(.*),3:(?<group1>abc),4:(?<group2>xyz)。 在表达式的后面部分引用前面的某一个子表达式分组叫做反向引用。 对于匿名分组的反向引用方法是"" 加上分组编号,对于命名分组,.Net中的引用方法是k<groupName>。 注意上面表格中第二个例子的表达式与w{5,}的区别,(w)1{4,}表示同一个字符重复至少5次以上,而w{5,}表示连续5个以上字符(不必是同一个字符)。 可以禁止正则表达式记录某个分组的匹配结果,这样的分组也就不会被编号,无法被反向引用。禁止分组使用(?:patterns),例如表达式abc(?:w{3})(d+)abc总共有两个分组,组号0为整个表达式,组号1为(d+)。 .Net中,1到9总是被当作反向引用;12这种类型,如果存在编号为12的分组,则作为反向引用,否则将12转义为ASCII字符,为了消除这种歧意,可以使用k<n>这种方式。 注意:.Net中分组命名时尖括号<,>可以使用单引号代替。 Lookahead,Lookbehind(正向预搜索、反向预搜索) 匹配当前的某一个子表达式时,可能需要根据前面或者后面出现的字符进行判断(即上下文环境),lookahead、lookbehind就是用于这个目的。 NFA以文本串作为有穷输入字符集Σ,从文本串逐个读取字符进行匹配,所以沿着字符读取方向的是lookahead,与字符读取方向相反则为lookbehind。 lookahead: (?=patterns),(?!patterns)。lookbehind: (?<=patterns),(?<!patterns)。 Options(正则表达式选项) JavaScript的正则表达式,使用/gi这样的开关控制正则表达式选项 。.Net中可以使用RegexOptions枚举进行全局设置,可以在分组表达式中使用(?imnsx-imnsx:patterns)方式,在这个分组内开启或禁用某些选项,也可以在表达式的中间使用(?imnsx-imnsx),从中间这个位置开始开启或禁用某些选项。全局RegexOptions的优先级低于嵌入方式。 嵌入方式中imnsx表示打开某种选项或选项的组合,前面添加减号"-"表示关闭这些选项。例如(?ix-ms)表示从这个位置开始,打开IgnoreCase、IgnorePatternWhiteSpace选项,关闭Multiline、Singleline选项。 嵌入方式修改正则表达式选项,也叫做Modifier。 NFA,DFA 正则表达式引擎的两种类型,NFA: Nondeterministic Finite Automata,DFA: Deterministic Finite Automaton。 NFA基于正则表达式去匹配文本(文本作为有穷字母表Σ),而DFA是基于文本去匹配正则表达式。DFA捏着文本串去比较正则式,看到一个子正则式,就把可能的匹配串全标注出来,然后再看正则式的下一个部分,根据新的匹配结果更新标注。而NFA是捏着正则式去比文本,吃掉一个字符,就把它跟正则式比较,匹配就记下来,然后接着往下干。一旦不匹配,就把刚吃的这个字符吐出来,一个个的吐,直到回到上一次匹配的地方。把多吃的字符吐出部分重新匹配的过程叫做backtracking(回溯)。 .Net中可以使用(?>patterns),禁止对这个子表达式进行回溯,即对输入字符backtracking过程中,一旦遇到这个子表达式已经匹配的字符,就停止backtracking。下面示例演示了这个效果: NFA、DFA主要对比: 1. DFA对文本串只扫描一次,速度快(时间与有穷字母表Σ的大小成线性关系),但特性少;NFA需反复扫描文本串,速度慢,但特性多。目前主要正则表达式引擎基本都是NFA,例如Perl、Java、.Net、Python、Td、Emacs,DFA的引擎有awk、egrep、lex。 2. NFA最左子正则式优先匹配,DFA是最长左子正则式优先匹配。 3. 只有NFA支持lazy、backtracking、backreference,NFA缺省使用greedy模式,NFA可能陷入递归陷阱导致性能极差。DFA只包含有穷状态,匹配过程中无法捕获子表达式(分组)的匹配结果,因此也无法支持backreference。 有另一种NFA引擎,叫做POSIX NFA引擎。传统NFA在backtracking时,只要当前位置上的最左子正则式匹配成功就停止;而POSIX NFA会继续尝试backtracking,以试图像DFA一样找到最长左子正则式。因此POSIX NFA速度更慢。 详细的NFA、DFA定义、算法,参考编译原理。 附加说明 1. 正则表达式在发展过程中,形成了很多版本的引擎,最基本的为grep,为了使grep具备更多特性而扩展出egrep, 目前使用的大多数正则引擎都是backtracking型的NFA。不同的正则表达式引擎之间,实现上多少也都有些差别,并且开发商还可能作出特有的扩展、语法形式等。因此,这就意味着并不是同一个正则表达式就会适用于所有的环境,例如.Net中的正则表达式,就不一定能在Java、Python、Unix中工作,这在网上查找正则表达式资源时需要注意。 2. 使用传统NFA,书写正则表达式需要特别注意性能问题,否则很容易导致死循环、性能极差等情况。 对正则表达式依赖性比较强的系统(大量使用正则做搜索匹配),最好完全掌握NFA->DFA算法,充分理解所使用的正则表达式引擎的原理和特性。 可以通过减少表达式中的模糊匹配、限制回溯等方法,将传统NFA的性能从多项式优化到线形关系,这完全取决于正则式的写法。 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
