Java正则表达式主要由两个类来实现:
java.util.regex.Pattern
java.util.regex.Matcher
Pattern用来将一个字符串形式的正则表达式编译成一个Pattern对象。Matcher执行匹配操作,解释模式串返回是否匹配目标字符串。典型使用方式如下:
Pattern p = Pattern.compile(“a*b”);
Matcher m = p.matcher(str);
boolean b = m.matches();
1. 构造正则表达式
字符
x | 字符x |
\\ | 反斜杠 |
\0n | 匹配8进制数0n |
\xhh | 匹配16进制数0xhh |
\t | 制表符Tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\a | 响铃 |
\e | 转义 |
\cx | 与x对应的控制字符 |
String str = "\\";
String regex = "\\\\";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
boolean ret = matcher.matches();
System.out.println(ret);
输出true。
字符类 [abc] | abc中的任意一个字符 |
[^abc] | 除abc之外的任意一个字符(否定) |
[a-zA-Z] | 从a到z或从A到Z的任意一个字符(并集) |
[a-d[m-p]] | 从a到d或从m到p的任意一个字符(并集) |
[a-z&&[def]] | def中的任意一个字符(交集) |
[a-z&&[^bc]] | 从a到z,除去b到c |
[a-z&&[^m-p]] | 从a到z,除去m到p |
. | 任意字符 |
\d | 数字:[0-9] |
\D | 非数字:[^0-9] |
\h | 水平空白符 |
\H | 非水平空白符[^\h] |
\s | 空白符(空格、Tab、换行、换页和回车) |
\S | 非空白符[^\s] |
\v | 垂直空白符 |
\V | 非垂直空白符[^\v] |
\w | 词字符[a-zA-Z_0-9] |
\W | 非词字符[^\w] |
^ | 行的起始 |
$ | 行的结束 |
\b | 词的边界 |
\B | 非词的边界 |
XY | X后面跟着Y |
X|Y | X或Y |
(X) | 捕获X,在一个组中捕获 |
量词
X? | X重复0次或1次 |
X* | X重复0次或多次 |
X+ | X重复1次或多次 |
X{n} X{n,} X{n,m} | X重复n次,X至少重复n次,X重复n到m次 |
? | 设默认(贪婪)匹配为reluctant匹配 |
+ | 设默认(贪婪)匹配为possessive匹配 |
Java在默认情况下,量词总是在匹配成功前提下匹配尽可能多的字符,称为贪婪匹配。后缀?改变为reluctant匹配,即在匹配成功前提下匹配尽可能少的字符。后缀+改变为possessive匹配,即使匹配失败也要匹配尽可能多的字符。
如模式(ab)*ab能匹配ababab,在能匹配成功的前提下,(ab)*匹配了abab,这里只有一种匹配方式,所以(ab)*?ab与(ab)*ab效果相同,但是(ab)*+ab就不能匹配ababab了,(ab)*+为possessive匹配,尽可能多的字符,直接匹配了ababab整个串,(ab)*+后面的ab没有与之匹配的字符了,匹配失败。2. 正则表达式使用
编译模式串
模式串使用compile函数编译得到Pattern,表示模式串,可以指定一些标识:
staticPattern compile(String regex, int flags);
Pattern.CASE_INSENSITIVE(?i) : 匹配时忽略大小写的区别,如果对Unicode字符忽略大小写,需要将该标识与UNICODE_CASE结合使用。
Pattern.MULTILINE(?m) : 默认情况下^和$匹配字符串的开始和结束,这种模式下,^和$既匹配一行的开始和结束,也匹配字符串的开始和结束。
Pattern.COMMENTS(?x) : 忽略正则表达式里的空白符(空格、Tab、回车),注释从#开始,一直到这行结束。
Pattern.UNIX_LINES(?d) : \n被认为是一行结束。匹配操作
Matcher类将模式串与目标串进行匹配。调用Pattern的matcher函数即可得到Matcher对象:
Pattern pattern = Pattern.compile(regex); // regex是模式串
Matcher matcher = pattern.matcher(str); // str是目标串
Matcher提供3个方法进行匹配操作:
boolean matches()
boolean lookingAt()
boolean find()
boolean find(int start)
函数返回布尔值,表示是否匹配成功。matches是将模式串匹配整个目标串,lookingAt是从目标串的起始位置开始匹配,不一定要匹配整个目标串。find函数找到一个匹配的子串即可,start参数指定开始匹配的位置,反复调用find可以得到所有匹配子串。应用中find函数使用较多。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularExpress {
public static void main(String[] args) {
String str = "a23opA23";
String regex = "a[0-9]*[a-z]+[A-Z]";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
boolean bMatches = matcher.matches();
matcher.reset();
boolean bLookingAt = matcher.lookingAt();
matcher.reset();
boolean bFind = matcher.find();
System.out.println("bMatches = " + bMatches + ", bLookingAt = " + bLookingAt + ", bFind = " + bFind);
}
}
运行结果为:
bMatches = false, bLookingAt = true, bFind = true
模式串
"a[0-9]*[a-z]+[A-Z]"不能匹配
"a23opA23",所以matches返回false,但是该模式串能匹配
"a23opA23"的子串
"a23opA",lookingAt和find返回true。
使用find可以找到目标串中所有匹配的子串:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularExpressFind {
public static void main(String[] args) {
String str = "It's a good day.";
String regex = "\\w+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while(matcher.find()) {
System.out.println(matcher.group());
}
}
}
运行结果为:
It
s
a
good
day
"\\w+"是指一个或多个词字符(a-zA-Z_0-9),因此该程序将输入字符串拆分为一个个单词,去掉了非词字符。
Region
匹配时还可以指定目标串的区域,区域是目标串的一个区间,所有匹配操作在该区间上进行,默认情况下是区域是整个目标串。Matcher region(int start, int end);
int regionStart();
int regionEnd();
区域不是说模式串要匹配到多少目标串才算成功(这是由matches、lookingAt、find三个函数实现),而是重新设定目标串的范围。如目标串abcdef的默认区域是(start=0,end=6),如果设定为(start=1,end=5)则表示只在子串bcde上进行匹配。感觉没什么用,如果需要,直接将输入串构造一个子串就可以了,当然如果输入的CharSequence是String,会有效率问题吧。
Groups
上面已经说过组的概念。如果模式串中有小括号,即模式串中有组,每次匹配之后不仅可以找到整个模式串匹配的目标子串,还可以找到模式串中每个组此时匹配的目标子串。主要有以下几个函数:
String group() // 返回上次匹配之后,模式串匹配的目标子串
String group(int group) // 返回上次匹配之后,模式串中指定组匹配的目标子串
int groupCount() // 返回模式串中组的数目
int start() // 返回上次匹配之后,匹配的目标子串起始位置
int start(int group) // 返回上次匹配之后,指定组匹配的目标子串的起始位置
int end() // 返回上次匹配之后,匹配的目标子串的结束位置
int end(int group) // 返回上次匹配之后,指定组匹配的目标子串的结束位置
注意这里的下标是左闭右开区间,结束位置是目标子串最后一个字符的下一个位置。group的下标是从1开始的,调用group(0)与调用group()效果相同,都表示整个正则表达式的匹配结果,start和end函数也是一样。先用matches匹配函数实验一下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularExpressGroup1 {
public static void main(String[] args) {
String str = "abce1234";
String regex = "([a-z]*(e))[0-9]*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
System.out.println("group count = " + matcher.groupCount());
if(matcher.matches()) {
for(int i=0; i<=matcher.groupCount(); i++) {
System.out.print("[" + matcher.group(i) + "]");
System.out.println();
}
}
}
}
运行结果为:
group count = 2
[abce1234]
[abce]
[e]
模式串"([a-z]*(e))[0-9]*"的组数显然是2。matches如果匹配成功,group(0)显然是整个目标串。第1组([a-z]*(e))匹配的是abce,第2组(e)匹配的是e。
下面的例子使用find匹配函数:import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularExpressGroup2 {
static public final String POEM = "Twas brillig, and the slithy toves\n" +
"Did gyre and gimble in the wabe.\n" +
"All mimsy were the borogoves,\n" +
"And the mome raths outgrabe.\n\n" +
"Beware the Jabberwock, my son,\n" +
"The jaws that bite, the claws that catch.\n" +
"Beware the Jubjub bird, and shun\n" +
"The frumious Bandersnatch.";
public static void main(String[] args) {
Matcher m = Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$").matcher(POEM);
while(m.find()) {
for(int j=0; j<=m.groupCount(); j++)
System.out.print("[" + m.group(j) + "]");
System.out.println();
}
}
}
运行结果为:
[the slithy toves][the][slithy toves][slithy][toves]
[in the wabe.][in][the wabe.][the][wabe.]
[were the borogoves,][were][the borogoves,][the][borogoves,]
[mome raths outgrabe.][mome][raths outgrabe.][raths][outgrabe.]
[Jabberwock, my son,][Jabberwock,][my son,][my][son,]
[claws that catch.][claws][that catch.][that][catch.]
[bird, and shun][bird,][and shun][and][shun]
[The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious][Bandersnatch.]
模式串中的(?m)指定Pattern.MULTILINE标识,^和$也匹配一行的开始和结束。\\s+匹配一个或多个空白符,\\S+匹配一个或多个非空白符。所以模式串
"(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$"匹配每行末尾的三个非空白符子串,中间有两段空白符。第1组
(\\S+)匹配每行倒数第三个非空白子串;第2组
((\\S+)\\s+(\\S+))匹配每行末尾两个非空白符子串,中间有一段空白符;第三组
(\\S+)匹配每行倒数第二个非空白子串;第四组
(\\S+)匹配每行最后一个非空白子串。