Java之正则表达式

33 篇文章 0 订阅

        正则表达式约定特殊的字符和规则,使用一串特殊字符的排列表示出某一字符串的特征,是用于进行文本匹配的工具。

一、java.util.regex包中主要包含三个类:

        1、Pattern类:正则表达式的编译表示形式。常用方法有:

                1>static Pattern compile(String regex):将指定的正则表达式编译到模式中。

Pattern p = Pattern.compile("a*b");

                2>Matcher matcher(String str):创建匹配指定字符串与本模式的匹配器。

Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaaaaaab");

                3>static boolean matches(String regex,String str):编译指定的正则表达式并与指定的字符串进行匹配。

Pattern.matches("a*b", "aaaaaaaaab");

                4>String[] split(String str,int limit):使用编译到模式中的正则表达式分隔指定的字符串指定的次数。如果为负数或0次,则分隔次数不作限制。如果为0时丢弃尾部的空字符串。

Pattern p = Pattern.compile("\\|");
String[] strs = p.split("a|b|c|d",-3);

        2、Matcher类:对输入字符串进行解释和匹配操作的引擎。需要调用Pattern对象的matcher方法来获得一个 Matcher对象。其有3种匹配方式:

                1>matches():整个匹配,只有整个字符序列完全匹配成功,才返回true,否则返回false。如果本部分匹配成功,将移动到下次匹配的位置。

                2>lookingAt():部分匹配,总是从第一个字符进行匹配,不管匹配成功与否都不再继续匹配。

                3>find():部分匹配,从当前位置开始匹配,找到一个匹配的子串,将移动下次匹配的位置。

        除了3种匹配方式外,常用的方法还有:

                4>int start():返回第一次匹配字符之后的偏移量,也就是说调用此方法之前必须先调用匹配方法。

                5>int end():返回最后匹配字符之后的偏移量,也就是说调用此方法之前必须先调用匹配方法。

                6>String group(int i):返回由之前匹配操作所匹配的指定捕获组编号的子序列,也就是说调用此方法之前必须先调用匹配方法。

                7>Matcher reset():重置匹配器。

                8>String replaceAll(String replacement):将与给定正则表达式相匹配的每个子序列替换为指定的值。

        3、PatternSyntaxException类:抛出未经检查的异常,表明正则表达式模式中的语法错误。

二、语法及规则约定:

        1、表示字符的方式:

                1>转义字符:在正则表达式中,有些字符表示特殊的含义,如果需要匹配这些字符本身,则不能再使用这些字符,而必须通过处理才能使用,否则它将是正则表达式中表示的特珠含义。

字符

正则表达式中的含义

处理方式

^

单独使用时表示匹配输入字符串的开始位置,如果放在[]中表示否定

\\^

$

匹配输入字符串的结尾位置,如果设置了RegExp对象的Multiline属性,则$也匹配‘\n'或‘\r'

\\$

()

表示捕获组的开始和结束

\\( \\)

[]

[]中指定的多个字符中的任意一个,或者指定一个范围

\\[ \\]

{}

限定某部分出现的次数

\\{ \\}

.

除换行符以外的任意一个字符

\\.

\

用在转义字符前可匹配特殊字符,用在捕获组编号前表示之前所匹配的指定编号的组,用在0xxx之前表示八进制表示的字符。由于"\"比较特殊,在String中也需要转义处理,所以String中的\是"\\",而正则匹配时则需要写成\\\\

\\\\

|

或者,两项之间的任一个

\\|

?

跟在字符后表示该字符不出现或出现1次,跟在数量限定符后表示非贪婪限制符

\\?

+

出现1次或多次

\\+

*

不出现或出现多次

\\*

                2>预定义字符使用正则表达式中已事先定义的字符表示特定的值:

字符

表示含义

等价表示

\\d

数字

[0-9]

\\D

非数字

[^0-9]

\\s

空白字符

 

\\S

非空白字符

[^\\s]

\\w

单词字符

[a-zA-Z_0-9]

\\W

非单词字符

[^\\w]

                3>字符缩略表示有些字符比较抽象,比如换行符等,可以用特殊的字符或者Unicode转义表示:

含义

表示

Unicode转义表示

制表符

\t

\u0009

换行符

\n

\u000A

回车符

\r

\u000D

换页符

\f

\u000C

报警符

\a

\u0007

转义符

\e

\u001B

垂直制表符

\v

\u000B

                4>进制表示使用八进制或十六进制表示字符:

                ①八进制表示:格式可以是\0n,\0nn或者\0mnn,其中0<=n<=7,0<=m<=3。

                ②十六进制表示:格式只能是\xhh,其中h只能在0-9、a-f或者A-F之间。

                5>Unicode转义表示格式是\uhhhh,其中h只能在0-9、a-f或者A-F之间。

                6>指定匹配某个固定字符直接将该字符放入正则表达式即可。

boolean b = Pattern.matches("A","AB");

                7>指定匹配某几个字符里的任意一个

                ①使用中括号[]将可选的字符括住即可。

boolean b = Pattern.matches("[ABC]","A");

                ②使用|

boolean b = Pattern.matches("A|B","A");

                8>指定匹配某个连续范围的某个字符在[]中用-指定一个范围

boolean b = Pattern.matches("[A-Za-z]","A");

                9>指定匹配某个范围的字符并且匹配另一个范围的字符两个范围使用&&连接并放在[]中

boolean b = Pattern.matches("[[a-z]&&[abcde]]","a");

                10>指定匹配不是某几个字符在不是的字符前加^并放在[]中

boolean b = Pattern.matches("[^a-c]", "b");

        2、表示字符出现的次数:

                1>字符不出现或出现1次:?

boolean b = Pattern.matches("ab?c", "ac");

                2>字符出现1次或多次:+

boolean b = Pattern.matches("ab+c", "abbbc");

                3>字符出现0次或多次:*

boolean b = Pattern.matches("ab*c", "ac");

                4>字符出现指定的次数:使用{}将次数括起来,比如字符出现3次。

boolean b = Pattern.matches("ab{3}c", "abbbc");

                5>字符出现的次数是一个范围:使用{}将次数括起来,最小值和最大值用,隔开,比如字符最少出现3次,最多出现7次

boolean b = Pattern.matches("ab{3,7}c", "abbbbbbbc");

                6>字符出现最少的指定次数:使用{}将最少次数括起来,去掉最大次数即可。比如字符最少出现3次

boolean b = Pattern.matches("ab{3,}c", "abbbc");

三、捕获组:

        捕获组就是把正则表达式中子表达式匹配的内容,保存到以数字编号的内存中或保存到显式命名的组里,方便后面引用。这种引用既可以是在正则表达式内部,也可以是在正则表达式外部。捕获组用括号表示。

        1、编号规则:按照“(”出现的顺序,从左到右,从1开始进行编号的。特殊地,编号为0的捕获组,指的是正则表达式整体。

        2、命名捕获组的命名规则:通过显式命名,可以通过组名方便的访问到指定的组,而不需要去一个个的数编号。同时避免了在正则表达式扩展过程中,捕获组的增加或减少对捕获组编号的变化导致的不可控情况。给捕获组命名的方式是:在要命名的组所在的括号中将”?<别名>“放在该组的正则表达式之前,此时<>中的名字即为该组的别名。比如给一个匹配格式为yyyy-MM-dd的日期的正则表达式进行捕获组命名,此时别名分别是year、date、month和day:

(?<year>\\d{4})-(?<date>(?<month>\\d{2})-(?<day>\\d\\d))

        3、在正则表达式内部使用捕获组(即反向引用):在要使用的捕获组之后使用\\加捕获组编号或者\\k<捕获组名>,即可将捕获组匹配的字符串作为正则表达式的一部分使用。比如:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\3");
Matcher m = p.matcher("2017-03-31-04");
System.out.println(m.matches());

或者:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\k<month>");
Matcher m = p.matcher("2017-03-31-04");
System.out.println(m.matches());

的结果返回false,因为正则表达式的最后部分使用了反向引用,即需要匹配之前匹配到的捕获组编号是3(别名是month)的内容(即字符串“2017-03-31-04”中的03)。所以:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\3");
Matcher m = p.matcher("2017-03-31-03");
System.out.println(m.matches());

或者:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\k<month>");
Matcher m = p.matcher("2017-03-31-03");
System.out.println(m.matches());

才会返回true。

四、匹配模式:当在字符后跟数量限定时(比如字符后跟?、+、*或者{2,5}),会涉及到匹配模式的问题。

        1、贪婪模式(Greediness):即最大匹配,是匹配模式的默认匹配方式。也就是说在数量限定词后不加匹配模式限定符时采用的是贪婪模式。例如:

Pattern p = Pattern.compile("a.*d");
Matcher m = p.matcher("a1bcda2bcda3bc");
boolean b = m.find();
System.out.println(b);
System.out.println(m.group());

此时会尝试匹配最多的字符,如果更多的字符不匹配时会返回上次匹配的字符,所以匹配的字符串是a1bcda2bcd而不是a1bcd。

        2、懒惰模式(Laziness):也叫勉强模式(Reluctant),即最小匹配。需要在数量限定词后加?即成为该模式。例如:

Pattern p = Pattern.compile("a.*?d");
Matcher m = p.matcher("a1bcda2bcda3bc");
boolean b = m.find();
System.out.println(b);
System.out.println(m.group());

此时会匹配最少的字符,只要找到即可,所以匹配的字符串是a1bcd而不a1bcda2bcd。

        3、占有模式(Possessive):即完全匹配。需要在数量限定词后加+即成为该模式。例如:

Pattern p = Pattern.compile("a.*+d");
Matcher m = p.matcher("a1bcda2bcda3bcd");
boolean b = m.find();
System.out.println(b);
System.out.println(m.group());

此时无法匹配,因为正则中a之后的.*已将字符串中a之后包括最后一位最后一位d在内的剩余的所有字符串都匹配了,而正则最后还有一个d,此时字符串中已无字符,也无法回退匹配相对少的子字符串,所以不能匹配。由此可以看出在占有模式中,正则中的?+或*+之后不能再指定某一明确固定的字符,否则将永远不匹配。

五、非捕获组(零宽断言):之所以是零宽,是因为它们像^和$一样匹配的只是位置,而不是字符。

        1、正向先行断言:(?=pattern)表示该位置之后的字符能够匹配pattern,比如字符串a regular expression,想要匹配regular中的re而不是expression中的re,可以使用re(?=gular),表明限定re右边必须是gular。如果使用正则:re(?=gular).则返回reg。因为断言不会匹配字符,它仅仅是一个位置。

        2、负向先行断言:(?!pattern)表示该位置之后的字符不能匹配pattern,比如字符串regex represents regular expression,想要匹配除regex和regular之外的re,可以使用re(?!g),表明限定re右边不能是g。负向和正向的区别,就在于该位置之后的字符能否匹配括号中的表达式。

        3、正向后行断言:(?<=pattern)表示该位置之前的字符能够匹配pattern,比如字符串regex represents regular expression,想要匹配单词内部的re,但不匹配单词开头的re,可以使用(?<=\w)re。后行和先行的区别在于,后行是从右往左扫描而先行是从左往右扫描。

        4、负向后行断言:(?<!pattern)表示该位置之前的字符不能匹配pattern,比如字符串regex represents regular expression,想要匹配单词开头的re,可以使用(?<!\w)re。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值