java 正则 (.)_详解Java 正则表达式 (上篇)

前言

在编程语言中,除了容器集合、多线程并发、IO流等等基础组件之外,还有一项专门处理字符串的“奇淫巧技”,这就是正则表达式。Perl语言正是凭借强大的正则表达式才异军突起的。

对于Java来说,自从jdk1.7之后,正则表达式也逐渐强大起来,不再是之前的半吊子了。现在的Java也非吴下阿蒙,其正则表达式也不逊于其他主流语言了。

本篇将详细描述Java正则表达式的用法、技巧等等,让你领略一下正则表达式的强大功能。

由于内容较多,整个文章分为上中下三篇。上篇内容介绍 基础替代符号; 中篇内容介绍Java的正则组件以及相关API; 下篇内容特别介绍转义字符的使用。

概述

何为正则表达式?英文称为Regular Expression。 其实这个翻译有点误导性,如果翻译成“规则表达式”,可能更好理解。

设想这么一个场景: 某一天,小舟同学在用word编辑文案,突然意识到某个单词好像拼写有误, 把Java 错写了Jave。 由于文字量比较大,手工一个个替换费时费力,好容易遗漏出错,但是好在word有自动替换功能,一键把全部Jave替换成Java即可。 但是,好景不长,小舟发现有些错误拼写成了Jave,还有些错误拼写成了Javo,还有Javq,Javz, Javd等等,还有连自己都不知道拼写成什么玩意的。

现在该怎么办? 小舟设想,如果有一个功能,能把以Jav开头的,不管第四个字母是什么,全部替换成Java。比如说,Jav*,以*代替任意字母,全部替换成Java。

这种以某些特殊字符来代替另外一些常用字符的表达方式,就称为正则表达式。另外,正则表达式是不限于编程语言的,在一些基本文字软件中,都有此类功能的集成。

基础替代符

在Java中, 把正则表达式的替代符号分为以下几类:

字符匹配符与范围控制符. (点) 任意字符

\s (反斜杠加小写字母s) 空白字符: 回车,制表,空格,换行

\S (反斜杠加大写字母S)非空白字符, 除了上面那四个空白字符

\w (反斜杠加小写字母w)单词字符: 小写字母a-z, 大写字母A-Z, 数字0-9, 下划线 _

\W (反斜杠加大写字母W)非单词字符,除了上面提到的那些单词字符

[ ] (中括号)范围字符[abc]表示字母abc中的任意一个

[a-z]表示字母a到z中的任意一个

[^abc] ^ 为取非, 除了abc, 剩下的全部字符

[abc[def]]这种写法为并集,意为abcdef中的任意一个

[abc&[bce]]& 表示与, 也就是交集。 abc 与上 bce, 最终结果为bc

数量控制符

上面介绍的字符匹配符和范围控制符,一般是与数量控制符组合使用,才能发挥真正威力。 而数量控制符号,分为贪婪型,勉强型,和占有型三种类型,每种类型在不同的场合有不同的用法。

贪婪型? 表示1个或0个。换句话说,表示要不然没有,要不然只有1个

* 表示0个或多个。

+ 表示1个或多个。

{n}表示正好n个

{n,m} 表示n到m个,这是一个左闭右闭区间

{n,}表示至少n个

勉强型

相比于贪婪型,勉强型只是多了一个?:

?? 表示1个或0个。换句话说,表示要不然没有,要不然只有1个

*? 表示0个或多个。

+? 表示1个或多个。

{n}?表示正好n个

{n,m}? 表示n到m个,这是一个左闭右闭区间

{n,}?表示至少n个

占有型

相比于贪婪型,占有型多了一个 +

?+ 表示1个或0个。换句话说,表示要不然没有,要不然只有1个

*+ 表示0个或多个。

++ 表示1个或多个。

{n}+表示正好n个

{n,m}+ 表示n到m个,这是一个左闭右闭区间

{n,}+表示至少n个

不同类型的区别

现在详细分析 贪婪型、勉强型和占有型的区别, 举例如下:

String s = "abcd&"; // 目标字符串

String regular = "\\w+\\w";// 贪婪型正则

String regular2 = "\\w+?\\w";// 勉强型正则

String regular3 = "\\w++\\w";// 占有型正则

Matcher m = Pattern.compile(regular).matcher(s);

// 调用方法去匹配字符串

if(m.find()) {

System.out.println(m.group());

}

分析:贪婪型。\w+, 意为匹配一个或多个单词字符,对于贪婪型,将尽量多的匹配字符,abcd全部匹配出来,直到遇到特殊字符&,匹配结束,然后用\w 去匹配特殊字符&,发现并不匹配,此时将会回溯,也就是说\w+只匹配abc三位,然后让最后一个\w去匹配d,最终\\w+\\w匹配出结果abcd.

勉强型。对于勉强型,会尽量少的匹配字符。既然,+表示一个或多个,那就只匹配一个,所以\w+?最终只匹配出一个a, 然后\w再去匹配一个b,最终匹配结果为ab。

占有型。 占有型和贪婪型很像,刚开始也会尽量多的匹配字符,但是占有型没有回溯机制,遇到不匹配的内容后,将自动停止而不是向前回溯。 所以 \\w++,自动匹配到abcd之后,遇到了&,发现不匹配,然后自动停止了,此时还有一个\w也和&不匹配,所以最终结果匹配失败,输出为空。

位置匹配符

位置匹配符,不匹配任何字符只匹配位置。也就是说,位置匹配符是零宽度的,只匹配位置。^ 匹配一行的开始位置

$ 匹配一行的结束位置

\b 匹配单词边界。 这里的单词指的就是, a-z, 0-9, _, 和\w不同的是,\b匹配的单词还包括汉字。 单词边界匹配,是匹配单词字符和非单词字符之间的位置。

\B匹配非单词边界。和\b,刚好相反, \B 匹配单词与单词之间的位置,或者非单词与非单词之间的位置。

\G匹配前一个匹配结束时的位置。

举例说明:

String s = "a=124_a=789_a=490";

String regular = "^a=\\d+";

比如说只想把开头的a=124找出来,而不关心后面的a,此时就可以用位置匹配符。^匹配一行的开始位置,a= 去匹配 a=, \\d+匹配出124, 所以最终可正确匹配出开始位置的a=124

还有一类特殊的匹配符,虽然严格意义上来说不能算是位置匹配符,但是由于这类匹配符也是零宽度的,所以本文章把这类匹配符归为位置匹配符。 这类特殊的匹配符,称为零宽度断言匹配。(?=exp) 零宽度向前匹配

(?<=exp) 零宽度向后匹配

(?!exp) 零宽度向前非匹配

(?

举例说明2:

// 目标字符串。 只想取出2前面的abc

String s = "abc1 abc1 abc2 abc3";

String regular = "\\w{3}(?=2)";

Matcher m = Pattern.compile(regular).matcher(s);

while(m.find()) {

System.out.println(m.group());

}

就像是$匹配字符串结尾一样,(?=2) 也是匹配字符串中为2的位置,然后再去检查(?=2)前面的\\w{3} 是否匹配abc, 匹配则输出结果。 之所以说是零宽度,是因为(?=2)只是起一个锚点定位作用,此时尾指针指向的不是2后面,而是c后面。

再比如说3:

String s = "aaaaaaaa";

String regular = "aa(?=aa)";

Matcher m = Pattern.compile(regular).matcher(s);

while(m.find()) {

System.out.println(m.group());

}

结果输出将是: aa aa aa

因为,第1次匹配,匹配到前4个a,输出aa; 第2次匹配,是从第2个a后开始向后,而不是第4个a后面,(?=aa) 只标定位置,不占有宽度; 以此类推,最终结果输出为aa aa aa

举例说明4:

对于零宽度向前匹配,和向后匹配方向相反:

// 目标字符串。 只要求输出2后面的abc

String s = "1abc 2abc 3abc";

String regular = "(?<=2)\\w{3}";

Matcher m = Pattern.compile(regular).matcher(s);

while(m.find()) {

System.out.println(m.group());

}

只要求输出2后面的abc,那么就可以用(?<=2)来进行锚点标定,匹配出其后面的字符串。

对于(?!exp) 和 (?

模式说明符(?i) 开启大小写忽略模式,但是只适用于ASCII字符

(?u) 开启utf-8编码模式

(?m) 开启多行匹配模式,.不匹配空白字符

(?s) 单行模式,.匹配任意字符,包括空白字符

(?d) 单行模式,.不匹配空白字符

举例说明:

String test2 = "I care not whether I can succeed\n"

+ "Since in choice of the distance\n"

+ "I simply travel in wind and rain\n"

+ "I care not whether I can win love\n"

+ "Since in deep love with a rose\n"

+ "I just unbosom myself bravely";

String reg = "(?m)\\w{3}$";

在默认情况下,^ 和 $ 只匹配全部字符串的开始与结束位置,但是如果用(?m)开启多行匹配模式之后, ^ 和 $ 将匹配每一行的开始与结束位置。所以说,上面上面那个举例正则(?m)\\w{3}$,将会匹配出每一行的最后三个字符。

你可能感兴趣的