JAVA 正则表达式,前瞻和后顾 反向前瞻 和 反向后顾

转:http://blog.csdn.net/xys_777/article/details/8642566

因为遇到一个这样的问题,所以看了一下Java的解释

在正则表达式里需要 匹配字符串,然后进行否定,就是在某位置,不出现匹配的字符串

http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html#cg

(?!X)    X, via zero-width negative lookahead

附转载文章:

java正则表达式找出不包含特定字符串 
博客分类: java 


参考资料 
http://www.imkevinyang.com/2009/08/%E4%BD%BF%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%89%BE%E5%87%BA%E4%B8%8D%E5%8C%85%E5%90%AB%E7%89%B9%E5%AE%9A%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%9D%A1%E7%9B%AE.html

正则基本知识 
http://xixian.iteye.com/blog/721147 

正则表达式中有前瞻(Lookahead)和后顾(Lookbehind)的概念,这两个术语非常形象的描述了正则引擎的匹配行为。需要注意一点,正则表达式中的前和后和我们一般理解的前后有点不同。一段文本,我们一般习惯把文本开头的方向称作“前面”,文本末尾方向称为“后面”。
但是对于正则表达式引擎来说,因为它是从文本头部向尾部开始解析的(可以通过正则选项控制解析方向),因此对于文本尾部方向,称为“前”,因为这个时候,正则引擎还没走到那块,而对文本头部方向,则称为“后”,因为正则引擎已经走过了那一块地方。

如下图所示: 

前瞻就是在正则表达式匹配到某个字符的时候,往“尚未解析过的文本”预先看一下,看是不是符合/不符合匹配模式。 

后顾,就是在正则引擎已经匹配过的文本看看是不是符合/不符合匹配模式。符合和不符合特定匹配模式我们又称为肯定式匹配和否定式匹配 

现在看它们的定义方法吧(零断言,不用管术语名称,翻译太拗口复杂了) 
Java代码 
//前瞻 
(?=exp)真正有用的部分,在这个位置之前,之前的数据需要匹配exp 
(?<=exp)真正有用的部分,在这个位置之后,之后的数据需要匹配exp 
//后顾 
(?!exp)真正有用的部分,在这个位置之前,之前的数据不匹配exp 
(?<!exp)真正有用的部分,在这个位置之后,之后的数据不匹配exp 


开始写不含特定字符的正则 

参考例子说明 
Java代码 

String reg="^(?!.*(不合谐)).*$";//用到了前瞻 
System.out.println("不管信不信,反正现在很不合谐".matches(reg));//false不通过 
System.out.println("不管信不信,反正现在非常合谐".matches(reg));//true通过 
System.out.println("不合谐在某国是普遍存在的".matches(reg));//false不通过 


上面就把含有特定字符的句子完全抹杀了,实现了完全和谐社会。。。。。 

上面例子是特定字符在任意位置出现都会匹配 
现在某国突然良心发现皇恩浩荡开放部分言论 
只想实现不以特定字符结尾的句子 
我们套用上面的例子,稍微改下 
Java代码 

String reg="^.*(?<!(不合谐))$";//用到了后顾 
System.out.println("不管信不信,反正现在很不合谐".matches(reg));//false不通过 
System.out.println("不管信不信,反正现在非常合谐".matches(reg));//true通过 
System.out.println("不合谐在某国是普遍存在的".matches(reg));//true通过 


现在第三条数据这么不和谐的数据也通过 
人民可以说些话了,某国也可以辟谣了 
五毛们也有工作量了,也多少增加了GDP的发展吧 
------------------------------- 
以上数据纯属虚构,如有雷同,纯属巧合

=============================================================================================

http://blog.csdn.net/iterzebra/article/details/6795857

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

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

==========================================================================================================

http://wygk2169.blog.163.com/blog/static/4537949920101195639462/

java环境下讲解。

java正则表达式主要设计Pattern,Mathcer,String这3个类的API





一、字符及字符串的表示法(有些是有许多表示法,但只以我的习惯为准) 



字符类

[abc] a、b 或 c(简单类)

[^abc] 任何字符,除了 a、b 或 c(否定)

[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)

[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)

[a-z&&[def]] d、e 或 f(交集)

[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)

[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)

***以上在java中使用时用双引号将它们括起来就可以了



预定义字符类

. 任何字符(与行结束符可能匹配也可能不匹配)

\d 数字:[0-9] (单个数字)

\D 非数字: [^0-9]

\s 空白字符:[ \t\n\x0B\f\r]

\S 非空白字符:[^\s]

\w 单词字符:[a-zA-Z_0-9]

\W 非单词字符:[^\w]



以上在java中使用时除了将它们用双引号括起来以为,还须加\转义,例如\d的实际用法是”\\d“



ession_r constructs, and what they match" border=0> POSIX 字符类(仅 US-ASCII)
\p{Lower} 小写字母字符:[a-z]
\p{Upper} 大写字母字符:[A-Z]
\p{ASCII} 所有 ASCII:[\x00-\x7F]
\p{Alpha} 字母字符:[\p{Lower}\p{Upper}]
\p{Digit} 十进制数字:[0-9]
\p{Alnum} 字母数字字符:[\p{Alpha}\p{Digit}]
\p{Punct} 标点符号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
\p{Graph} 可见字符:[\p{Alnum}\p{Punct}]
\p{Print} 可打印字符:[\p{Graph}\x20]
\p{Blank} 空格或制表符:[ \t]
\p{Cntrl} 控制字符:[\x00-\x1F\x7F]
\p{XDigit} 十六进制数字:[0-9a-fA-F]
\p{Space} 空白字符:[ \t\n\x0B\f\r]





以上字符串要加双引号和反斜杠号





ession_r constructs, and what they match" border=0> Greedy 数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次





用法也是将它们用双引号括起来



ession_r constructs, and what they match" border=0> Logical 运算符
XY X 后跟 Y
X|Y X 或 Y
(X) X,作为捕获组




用法也是将它们用双引号括起来



注意:正则表达式则有在方法参数明确是以正则表达式为参考时才使用,否则如果方法参数是普通字符序列时是不起作用的。 



二、相关的API

1、String类

String类的有些函数包含了正则表达式的概念以用法,但须注意的是有些函数的参数虽然是字符串型,但要具体分析,如果它只是普通字符串,那就按一般的处理,如果它是一个正则表达式则须注意。某些函数要求你的参数必须是正则表达式型的。

1-1、boolean contains(CharSequence s)

当且仅当此字符串包含指定的 char 值序列时,返回 true。

该函数的参数只是普通的字符串

String s="abc.e",s.contains(".")为true

1-2、String replace(CharSequence target, CharSequence replacement)

使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。

该函数的参数也是普通字符串

因此 String s="abc990",s.replace(“\\d”,"A")时,结果是不变的

1-3、String replaceAll(String regex, String replacement)

使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。

该函数的首个参数是正则表达式

因此String s="abc990",s.replaceAll(“\\d”,"A")时,其返回值(不是s1本身)结果为"abcAAA".

1-4、String[] split(String regex)

根据给定正则表达式的匹配拆分此字符串。

1-5、boolean startsWith(String prefix)

测试此字符串是否以指定的前缀开始。

1-6、boolean endsWith(String suffix)

测试此字符串是否以指定的后缀结束。 

注意 以上2个函数参数是普通字符串







2、Pattern类

获取其实例

2-1、static Pattern compile(String regex)

将给定的正则表达式编译到模式中。



2-2、String[] split(CharSequence input)

围绕此模式的匹配拆分给定输入序列。

2-3、Matcher matcher(CharSequence input)

创建匹配给定输入与此模式的匹配器。

2-4、static boolean matches(String regex, CharSequence input)

编译给定正则表达式并尝试将给定输入与其匹配。

例如 Pattern.matches(“\\d”,"3")将返回true







3、Mathcer类

3-1、Pattern pattern()

返回由此匹配器解释的模式。

3-2、boolean matches()

尝试将整个区域与模式匹配。

完全匹配时才会为true



3-3、 Matcher usePattern(Pattern newPattern)

更改此 Matcher 用于查找匹配项的 Pattern。

3-4、 boolean find()

尝试查找与该模式匹配的输入序列的下一个子序列。

使用以下形式将可以遍历所有的序列,从而进行其他操作

boolean find=matcher.find();

while(find 

find=matcher.find();

}

3-5、Matcher appendReplacement(StringBuffer sb, String replacement)

实现非终端添加和替换步骤。

此函数将当前查找到的匹配相用replacement代替,兵将replacement和之前的字符串存储在

sb中,例如输入序列是"abcDseD",正则表达式是"D",replacement是"A",第一次调用find方法后

然后执行此函数则sb的内容为"abcA".(sb的值初始化为调用默认构造函数生成) 

3-6、StringBuffer appendTail(StringBuffer sb)

实现终端添加和替换步骤。

执行此函数将3-5中的sb与其后的字符串连起来,引用上面例子,如果在3-5函数后调用此函数则sb的结果为"abcAseD".



例子,将"abc03DW9"中的数字全部换为"A",可用以下代码得到

Pattern pattern=Pattern.compile(“\\d”);

Matcher matcher=pattern.matcher("abc03DW9");

boolean find;

StringBuffer sb=new StringBuffer();

while(find){

matcher.appendReplacement(sb,"A");

find=matcher.find();

}

matcher.appendTail(sb);

到此步已获结果

+++++++++++++++++++++++++++++++++++++++++++++++++

Java正则表达式应用总结 
2009-07-17 08:45:17
标签:正则表达式 休闲 职场 
版权声明:原创作品,谢绝转载!否则将追究法律责任。 
Java正则表达式应用总结

一、概述

正则表达式是Java处理字符串、文本的重要工具。

Java对正则表达式的处理集中在以下两个两个类:
java.util.regex.Matcher 模式类:用来表示一个编译过的正则表达式。
java.util.regex.Pattern 匹配类:用模式匹配一个字符串所表达的抽象结果。
(很遗憾,Java Doc并没有给出这两个类的职责概念。)

比如一个简单例子:
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

/** 
* 正则表达式例子 

* @author leizhimin 2009-7-17 9:02:53 
*/ 
public class TestRegx { 
public static void main(String[] args) { 
Pattern p = Pattern.compile("f(.+?)k"); 
Matcher m = p.matcher("fckfkkfkf"); 
while (m.find()) { 
String s0 = m.group(); 
String s1 = m.group(1); 
System.out.println(s0 + "||" + s1); 

System.out.println("---------"); 
m.reset("fucking!"); 
while (m.find()) { 
System.out.println(m.group()); 


Pattern p1 = Pattern.compile("f(.+?)i(.+?)h"); 
Matcher m1 = p1.matcher("finishabigfishfrish"); 
while (m1.find()) { 
String s0 = m1.group(); 
String s1 = m1.group(1); 
String s2 = m1.group(2); 
System.out.println(s0 + "||" + s1 + "||" + s2); 


System.out.println("---------"); 
Pattern p3 = Pattern.compile("(19|20)\\d\\d([- /.])(0[1-9]|1[012])\\2(0[1-9]|[12][0-9]|3[01])"); 
Matcher m3 = p3.matcher("1900-01-01 2007/08/13 1900.01.01 1900 01 01 1900-01.01 1900 13 01 1900 02 31"); 
while (m3.find()) { 
System.out.println(m3.group()); 


}

输出结果:
fck||c 
fkk||k 
--------- 
fuck 
finish||in||s 
fishfrish||ishfr||s 
--------- 
1900-01-01 
2007/08/13 
1900.01.01 
1900 01 01 
1900 02 31 

Process finished with exit code 0

二、一些容易迷糊的问题

1、Java对反斜线处理的问题

在其他语言中,\\表示要插入一个字符\;
在Java语言中,\\表示要插入正则表达式的反斜线,并且后面的字符有特殊意义。

看API文档:
预定义字符类 
. 任何字符(与行结束符可能匹配也可能不匹配) 
\d 数字:[0-9] 
\D 非数字: [^0-9] 
\s 空白字符:[ \t\n\x0B\f\r] 
\S 非空白字符:[^\s] 
\w 单词字符:[a-zA-Z_0-9] 
\W 非单词字符:[^\w] 

但是看看上面程序,对比下不难看出:
\d在实际使用的时候就写成了 \\d;


在Java正则表达式中,如果要插入一个\字符,则需要在正则表达式中写成\\\\,原因是下面的APIDoc定义\\表示一个反斜线。
但是如果在正则表示式中表示回车换行等,则不需要多添加反斜线了。比如回车\r就写作\r.

字符 
x 字符 x 
\\ 反斜线字符 
\0n 带有八进制值 0 的字符 n (0 <= n <= 7) 
\0nn 带有八进制值 0 的字符 nn (0 <= n <= 7) 
\0mnn 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7) 
\xhh 带有十六进制值 0x 的字符 hh 
\uhhhh 带有十六进制值 0x 的字符 hhhh 
\t 制表符 ('\u0009') 
\n 新行(换行)符 ('\u000A') 
\r 回车符 ('\u000D') 
\f 换页符 ('\u000C') 
\a 报警 (bell) 符 ('\u0007') 
\e 转义符 ('\u001B') 
\cx 对应于 x 的控制符 

2、Matcher.find():尝试查找与模式匹配的字符序列的下一个子序列。此方法从字符序列的开头开始,如果该方法的前一次调用成功了并且从那时开始匹配器没有被重置,则从以前匹配操作没有匹配的第一个字符开始,即如果前一次找到与模式匹配的子序列则这次从这个子序列后开始查找。

3、Matcher.matchers():判断整个字符序列与模式是否匹配。当连续用Matcher对象检查多个字符串时候,可以使用
Matcher.reset():重置匹配器,放弃其所有显式状态信息并将其添加位置设置为零。
或者Matcher.reset(CharSequence input) 重置此具有新输入序列的匹配器。
来重复使用匹配器。

4、组的概念,这个概念很重要,组是用括号划分的正则表达式,可以通过编号来引用组。组号从0开始,有几对小括号就表示有几个组,并且组可以嵌套,组号为0的表示整个表达式,组号为1的表示第一个组,依此类推。
例如:A(B)C(D)E正则式中有三组,组0是ABCDE,组1是B,组2是D;
A((B)C)(D)E正则式中有四组:组0是ABCDE,组1是BC,组2是B;组3是C,组4是D。

int groupCount():返回匹配其模式中组的数目,不包括第0组。
String group():返回前一次匹配操作(如find())的第0组。
String group(int group):返回前一次匹配操作期间指定的组所匹配的子序列。如果该匹配成功,但指定组未能匹配字符序列的任何部分,则返回 null。
int start(int group):返回前一次匹配操作期间指定的组所匹配的子序列的初始索引。
int end(int group):返回前一次匹配操作期间指定的组所匹配的子序列的最后索引+1。

5、匹配的范围的控制
最变态的就要算lookingAt()方法了,名字很让人迷惑,需要认真看APIDoc。

start() 返回以前匹配的初始索引。
end() 返回最后匹配字符之后的偏移量。

public boolean lookingAt()尝试将从区域开头开始的输入序列与该模式匹配。 
与 matches 方法类似,此方法始终从区域的开头开始;与之不同的是,它不需要匹配整个区域。 
如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。 
返回:
当且仅当输入序列的前缀匹配此匹配器的模式时才返回 true。

6、Pattern标记

Pattern类的静态方法
static Pattern compile(String regex, int flags) 
将给定的正则表达式编译到具有给定标志的模式中。 
其中的flags参数就是Pattern标记,这个标记在某些时候非常重要。

Pattern.CANON_EQ 
启用规范等价。 
Pattern.CASE_INSENSITIVE 
启用不区分大小写的匹配。 
Pattern.COMMENTS 
模式中允许空白和注释。 
Pattern.DOTALL 
启用 dotall 模式。 
Pattern.LITERAL 
启用模式的字面值分析。 
Pattern.MULTILINE 
启用多行模式。 
Pattern.UNICODE_CASE 
启用 Unicode 感知的大小写折叠。 
Pattern.UNIX_LINES 
启用 Unix 行模式。 

三、字符串的替换

String.replace(char oldChar, char newChar) 
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 而生成的。 
String.replace(CharSequence target, CharSequence replacement) 
使用指定的字面值替换序列替换此字符串匹配字面值目标序列的每个子字符串。 
String.replaceAll(String regex, String replacement) 
使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的每个子字符串。 
String.replaceFirst(String regex, String replacement) 
使用给定的 replacement 字符串替换此字符串匹配给定的正则表达式的第一个子字符串。 

StringBuffer.replace(int start, int end, String str) 
使用给定 String 中的字符替换此序列的子字符串中的字符。
StringBuilder.replace(int, int, java.lang.String)
使用给定 String 中的字符替换此序列的子字符串中的字符。

Matcher.replaceAll(String replacement) 
替换模式与给定替换字符串相匹配的输入序列的每个子序列。 
Matcher.replaceFirst(String replacement) 
替换模式与给定替换字符串匹配的输入序列的第一个子序列。 

四、字符串的切分

String[] split(String regex) 
根据给定的正则表达式的匹配来拆分此字符串。 
String[] split(String regex, int limit) 
根据匹配给定的正则表达式来拆分此字符串。

当然,还有一个StringTokenizer类,可以用来切分字符串,但是现在SUN已经不推荐使用了。
转变下思路,其实用正则表达式也可以达到将字符串切分为段的目的。

五、没有提到的

正则表达式的API简单好用,没太多复杂的地方,并非不重要,正则表达式最大的难点在于熟练书写正则表达式。
有关正则表达式的规范,在Pattern类APIdoc中都有非常详细的介绍,而且条理清晰,在此就不赘述了。

相关推荐