正则表达式 java.util.regex Pattern

时间:2023-01-07 14:01:40

  最近写字符串处理程序用到了Java正则表达式。其中写了个  \\s*:\\s*|(?<!Egress)\\s{2,}  。其语义是, “任意个空格+:+任意个空格”的形式或者 “两个以上的空格,但是前边不能是Egress”。

  为了写正则表达式,特意查看了JDK帮助文档,其中有一段:


Special constructs (non-capturing)
(?:X) X, as a non-capturing group
(?idmsux-idmsux)  Nothing, but turns match flags i d m s u x on - off
(?idmsux-idmsux:X)   X, as a non-capturing group with the given flags i d m s u x on - off
(?=X) X, via zero-width positive lookahead
(?!X) X, via zero-width negative lookahead
(?<=X) X, via zero-width positive lookbehind
(?<!X) X, via zero-width negative lookbehind
(?>X) X, as an independent, non-capturing group

特殊构造(非捕获)
(?:X) X,作为非捕获组
(?idmsux-idmsux)  Nothing,但是将匹配标志idmsux on - off
(?idmsux-idmsux:X)   X,作为带有给定标志 i d m s u x on - off
(?=X) X,通过零宽度的正 lookahead
(?!X) X,通过零宽度的负 lookahead
(?<=X) X,通过零宽度的正 lookbehind
(?<!X) X,通过零宽度的负 lookbehind
(?>X) X,作为独立的非捕获组


好家伙,这文档写的,英文不是中文,中文也不是中文。不得不解释下鸟:


lookahead和lookbehind也就是先行和后行。

先行指的是,在进行断言的时候,字符串匹配引擎指针会被向前移动以进行断言(由于zero-width,所以之后会再被移回)。

后行指的是,在进行断言的时候,字符串匹配引擎指针会被向后移动以进行断言(由于zero-width,所以之后会再被移回)。

zero-width也就是零宽度。

指的是断言前后不会改变当前字符串匹配引擎的指针位置。

positive 和negative  表示是预测匹配还是不匹配。


因此:

1 (?=X),预测其前边字串匹配位置后为X。

(?=X)的意思就是预测匹配位置之后出现的应该是X。如果符合则匹配上。在(?=X)后边带上不以X为开始的任意字符串都将导致该表达式不能被匹配。因为预测不改变当前字符串搜索的指针(零宽度)。

例如:

表达式"a(?=b)c"不能匹配"abc",也不能匹配"ac"、"ab"。而且不能匹配任何字符串。因为其预测匹配a的位置后,应该是b,但是,又要求a之后是c。

表达式"a(?=b)bc"是可以匹配"abc"的,但是不能匹配"ab"和"ac"。


如果(?=X)前没有字符,则表示在任何位置处(因为匹配的是任何字串),预测其后是X。

例如:

(?=b)bc 可以匹配“bc"中的bc

(?=b)b可以匹配"bc"中的b

(?=b)可以匹配"bc"中的最开始位置


(?=X)的工作过程如下:

左边的表达式匹配后,字符串引擎匹配位置右移,然后引擎发现是一个lookahead断言,因此判断该位置后是否符合断言(为了不改变位置指针,需要断言后回溯),如果符合,继续进行匹配。

由于断言是向前的,所以为先行;在断言前后,字符串引擎匹配位置不改变,所以为零宽度;又因为是要求预测为“符合”,因此为正向;因此称为零宽度正向先行断言。

例如,a(?=b)b匹配abc的时候为:

a符合a,之后发现是先行断言,判断该位置即b处,为b,符合断言,之后从该位置即a(因为零宽度先行断言的回溯使得位置指针不因断言改变)继续匹配,发现可以匹配b,因此匹配结果为ab。


2,(?!X) 预测其前边字串匹配位置后不是X。

(?!X)的工作过程与(?=X)相似,其是零宽度先行负向断言。


3,(?<=X) 预测其后字串匹配位置前的为X。因此其前边的字串必须是以X为结尾的。

其是零宽度正向后行断言。

例如a(?=b)c无法匹配任何字符串,因为其预测c之前是b,而又要求匹配a。

其工作过程为:

左边匹配后,字符串引擎匹配位置右移,然后发现表达式是lookbehind断言,因此,断言从左边一定的位置与lookbehind表达式进行匹配以进行lookbehind断言(注意字符串引擎匹配位置指针不会变动,依然是在断言左边的表达式匹配完后的位置上),如果断言成功,则继续匹配下一个。


例如:

b(?<=b)c 可以匹配“abc"。

a不是b,下一个b是b,发现是后行断言,当前位置(在c处)后移一个,到b处,判断该处是b,符合断言,继续从b后即c开始匹配,符合c,匹配结果为bc。


4,(?<!X) 预测其后字符匹配位置前的不为X。因此表达式前边的字串不能以X为结尾,否则将永远不能匹配上字串。

其是零宽度负向后行断言。


5,(?:X) 预测其后是X,但是不进行字符位置回溯,因此会改变字符串引擎匹配位置指针。

例如:

(?=b)b可以匹配“bc"中的b;但是(?:b)b则不能匹配"bc"中的b,因为其预测为b后,指针到达了c,c不是b,因此不符合;(?:b)c则可以匹配"bc"中的bc。


6,(?>X)预测其后是X,但是不进行字符位置回溯,因此会改变字符串引擎匹配位置指针。例如:

(?>b)b则不能匹配"bc"中的b;(?>b)c则可以匹配"bc"中的bc。



注意:Java后行断言不支持其中使用*、+、?等元字符,但是支持{n,m},因为前边那些使得无法确定后行断言后要回溯的位置多少。


参考:

http://www.regular-expressions.info/lookaround.html