Java 提供了 java.util.regex 包来与正则表达式进行模式匹配。Java 正则表达式和 Perl 编程语言非常相似,也容易学习。
正则表达式是一个特殊的字符序列,有助于你用一种专门的语法模式来匹配或找到其他字符串或字符串集。他们可以用来搜索编辑或是操纵文本和数据。
java.util.regex 包主要包含了下面的三个类:
Pattern 类:一个 Pattern 对象是正则表达式编译表示。 Pattern 类没有提供公共的构造函数。要创建一个 Pattern 对象,你必须首先调用他的公用静态编译方法来获得 Pattern 对象。这些方法的第一个参数是正则表达式。
Matcher 类:一个 Matcher 对象是用来解释模式和执行与输入字符串相匹配的操作。和 Pattern 类一样 Matcher 类也是没有构造方法的,你需要通过调用 Pattern 对象的 matcher 方法来获得 Matcher 对象。
- PatternSyntaxException: 一个 PatternSyntaxException 对象是一个不被检查的异常,来指示正则表达式中的语法错误。
捕获组
捕获组是一种将多个字符抽象为一个处理单元的方法。他们通过用括号将字符分组来创建。举个例子,正则表达式(dog)创建一个组包含字符 "d","o"和"g"。
捕获组通过从左向右计算括号的个数来进行计数。在正则表达式((A)(B(C)))中,这里有四个组:
((A)(B(C)))
(A)
(B(C))
- (C)
为了在表达式中计算有多少个组,可以调用 matcher 对象中的 groupCount 方法。 groupCount 方法返回一个 int 类型来显示正则表达式中的捕获组的数量。
这里也有特殊的组,组0总是代表了整个表达式。这个组不包含在 groupCount 负责的所有组内。
示例
下面的例子展现了如何从给定的字符数字串中找出数字串
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
public static void main( String args[] ){
// String to be scanned to find the pattern.
String line = "This order was placed for QT3000! OK?";
String pattern = "(.*)(\\d+)(.*)";
// Create a Pattern object
Pattern r = Pattern.compile(pattern);
// Now create matcher object.
Matcher m = r.matcher(line);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
System.out.println("Found value: " + m.group(2) );
} else {
System.out.println("NO MATCH");
}
}
}
这将会得到下面的结果
Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT300
Found value: 0
正则表达式语法
这里的表格记录了 java 中可用的所有正则表达式的元字符语法:
子表达式 | 匹配对应 |
---|---|
^ | 匹配一行的开头 |
$ | 匹配一行的结尾 |
. | 匹配除了换行符的任何单个字符,也可以利用 m 选项允许它匹配换行符 |
[...] | 匹配括号内的任意单个字符。 |
[^...] | 匹配不在括号内的任意单个字符。 |
\A | 整个字符串的开始 |
\z | 整个字符串的结束 |
\Z | 整个字符串的结束,除了最后一行的结束符 |
re* | 匹配0或者更多的前表达事件 |
re+ | 匹配1个或更多的之前的事件 |
re? | 匹配0或者1件前表达事件 |
re{ n} | 匹配特定的n个前表达事件 |
re{ n,} | 匹配n或者更多的前表达事件 |
re{ n, m} | 匹配至少n最多m件前表达事件 |
a| b | 匹配a或者b |
(re) | 正则表达式组匹配文本记忆 |
(?: re) | 没有匹配文本记忆的正则表达式组 |
(?> re) | 匹配无回溯的独立的模式 |
\w | 匹配单词字符 |
\W | 匹配非单词字符 |
\s | 匹配空格。等价于 [\t\n\r\f] |
\S | 匹配非空格 |
\d | 匹配数字. 等价于 [0-9] |
\D | 匹配非数字 |
\A | 匹配字符串的开始 |
\Z | 匹配字符串的末尾,如果存在新的一行,则匹配新的一行之前 |
\z | 匹配字符串的末尾 |
\G | 匹配上一次匹配结束的地方 |
\n | 返回参考捕获组号“N” |
\b | 不在括号里时匹配单词边界。在括号里时匹配退格键 |
\B | 匹配非词边界 |
\n, \t, etc. | 匹配换行符,回车符,制表符,等 |
\Q | 引用字符的初始,结束于\E |
\E | 结束由\Q开始的引用 |
元字符 | 描述 |
\ | 将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\\n”匹配\n。“\n”匹配换行符。序列“\\”匹配“\”而“\(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。 |
^ | 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。 |
* | 匹配前面的子表达式任意次。例如,zo*能匹配“z”,“zo”以及“zoo”。*等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。 |
{n} | n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。 |
{n,} | n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。 |
{n,m} | m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。 |
? | 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。 |
.点 | 匹配除“\r\n”之外的任何单个字符。要匹配包括“\r\n”在内的任何字符,请使用像“[\s\S]”的模式。 |
(pattern) | 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。 |
(?:pattern) | 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。 |
(?=pattern) | 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。 |
(?!pattern) | 正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。 |
(?<=pattern) | 反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。 |
(?<!pattern) | 反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。 |
x|y | 匹配x或y。例如,“z|food”能匹配“z”或“food”或"zood"(此处请谨慎)。“(z|f)ood”则匹配“zood”或“food”。 |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。 |
[^xyz] | 负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。 注意:只有连字符在字符组内部时,并且出现在两个字符之间时,才能表示字符的范围; 如果出字符组的开头,则只能表示连字符本身. |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。 |
\B | 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。 |
\cx | 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。 |
\d | 匹配一个数字字符。等价于[0-9]。 |
\D | 匹配一个非数字字符。等价于[^0-9]。 |
\f | 匹配一个换页符。等价于\x0c和\cL。 |
\n | 匹配一个换行符。等价于\x0a和\cJ。 |
\r | 匹配一个回车符。等价于\x0d和\cM。 |
\s | 匹配任何不可见字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。 |
\S | 匹配任何可见字符。等价于[^ \f\n\r\t\v]。 |
\t | 匹配一个制表符。等价于\x09和\cI。 |
\v | 匹配一个垂直制表符。等价于\x0b和\cK。 |
\w | 匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。 |
\W | 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。 |
\xn | 匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。正则表达式中可以使用ASCII编码。 |
\num | 匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。 |
\n | 标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。 |
\nm | 标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。 |
\nml | 如果n为八进制数字(0-7),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。 |
\un | 匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。 |
\< \> | 匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the\>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。 |
\( \) | 将 \( 和 \) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。 |
| | 将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。 |
+ | 匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。 |
? | 匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。 |
{i} {i,j} | 匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]{3} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]{4,6} 匹配连续的任意4个、5个或者6个数字 |
Matcher 类的方法
这里列出了有用的实例方法
index 方法
index方法提供有用的指标值,精确地显示输入字符串中相匹配的位置:
SN | 方法描述 |
---|---|
1 | public int start() 返回之前匹配开始索引 |
2 | public int start(int group) 返回被之前匹配操作得出的组捕获的子序列 |
3 | public int end() 返回在最后一个字符匹配之后的偏移量 |
4 | public int end(int group) 返回在之前匹配操作得出的组捕获的子序列之后的偏移量 |
Study 方法
Study 方法根据输入字符串返回一个布尔类型数据来指示该模式是否被找到。
SN | 方法描述 |
---|---|
1 | public boolean lookingAt() 试图匹配输入序列,从模式的起始位置开始 |
2 | public boolean find() 试图找到下一个输入序列的子序列来进行模式匹配 |
3 | public boolean find(int start) 重置匹配,并且试图找到下一个从某个特定位置开始的输入序列的子序列来进行模式匹配 |
4 | public boolean matches() 试图去匹配模式的整个区域 |
Replacement 方法
Replacement 方法是在一个输入字符串中替换文本的有效方法。
SN | 方法描述 |
---|---|
1 | public Matcher appendReplacement(StringBuffer sb, String replacement) 实现一个无目的的添加和代替步骤 |
2 | public StringBuffer appendTail(StringBuffer sb) 实现一个有目的的添加和代替步骤 |
3 | public String replaceAll(String replacement) 代替每一个输入序列的子序列,与给出的代替字符串的模式匹配 |
4 | public String replaceFirst(String replacement) 代替第一个输入序列的子序列,与给出的代替字符串的模式匹配 |
5 | public static String quoteReplacement(String s) 返回一个特定字符串逐字替换的字符串。这个方法产生了一个字符串将作为文本替换的 Matcher 类的 appendreplacement 方法 |
start 和 end 方法
下面是一个例子,计算 "cats" 在输入字符串中出现的次数:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static final String REGEX = "\\bcat\\b";
private static final String INPUT =
"cat cat cat cattie cat";
public static void main( String args[] ){
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // get a matcher object
int count = 0;
while(m.find()) {
count++;
System.out.println("Match number "+count);
System.out.println("start(): "+m.start());
System.out.println("end(): "+m.end());
}
}
}
这是产生的结果:
Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
Match number 4
start(): 19
end(): 22
你可以看到这个例子用次边界来确保字母 "c""a""t" 不仅仅是一个长单词子串。它也给出了一些关于在输入字符串中匹配位置的有用的信息。
start方法返回值是之前end方法返回值加1。
matches 和 lookingAt 方法
matches 和 lookingAt 方法都是按照一定的模式匹配输入序列,两种方法不同的是 matches 方法需要匹配整个输入序列,找出其中不同的。
两种方法都总是开始于输入字符串的起始位置。这里是一个例子:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static final String REGEX = "foo";
private static final String INPUT = "fooooooooooooooooo";
private static Pattern pattern;
private static Matcher matcher;
public static void main( String args[] ){
pattern = Pattern.compile(REGEX);
matcher = pattern.matcher(INPUT);
System.out.println("Current REGEX is: "+REGEX);
System.out.println("Current INPUT is: "+INPUT);
System.out.println("lookingAt(): "+matcher.lookingAt());
System.out.println("matches(): "+matcher.matches());
}
}
这是产生的结果:
Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
lookingAt(): true
matches(): false
replaceFirst 方法和 replaceAll 方法
replaceFirst 和 replaceAll 方法替换了匹配给定正则表达式的文本。正如它们的名字所表明的,replaceFirst 替换第一个情况,而 replaceAll 替换所有的情况。
这里是解释功能的例子:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static String REGEX = "dog";
private static String INPUT = "The dog says meow. " +
"All dogs say meow.";
private static String REPLACE = "cat";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
INPUT = m.replaceAll(REPLACE);
System.out.println(INPUT);
}
}
以上将会产生如下结果:
The cat says meow. All cats say meow.
appendReplacement 和 appendTail 方法
Matcher 类还提供了 appendReplacement 和 appendTail 两种方法来替换文本。
这里是解释功能的例子:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static String REGEX = "a*b";
private static String INPUT = "aabfooaabfooabfoob";
private static String REPLACE = "-";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
StringBuffer sb = new StringBuffer();
while(m.find()){
m.appendReplacement(sb,REPLACE);
}
m.appendTail(sb);
System.out.println(sb.toString());
}
}
以上将会产生如下结果:
-foo-foo-foo-
PatternSyntaxException Class 方法
PatternSyntaxException 是一个未检查的、在正则表达式模式指示语法错误的特例。PatternSyntaxException 类提供了以下的方法来帮助你找出问题所在:
SN | 方法描述 |
---|---|
1 | public String getDescription() 检索错误的描述 |
2 | public int getIndex() 检索误差指标 |
3 | public String getPattern() 检索错误的正则表达式模式 |
4 | public String getMessage() 返回一个包含语法错误的描述及其指标的多行字符串、错误的正则表达式模式以及显示模式里误差指标 |
常用正则表达式
规则 | 正则表达式语法 |
一个或多个汉字 | ^[\u0391-\uFFE5]+$ |
邮政编码 | ^[1-9]\d{5}$ |
QQ号码 | ^[1-9]\d{4,10}$ |
邮箱 | ^[a-zA-Z_]{1,}[0-9]{0,}@(([a-zA-z0-9]-*){1,}\.){1,3}[a-zA-z\-]{1,}$ |
用户名(字母开头 + 数字/字母/下划线) | ^[A-Za-z][A-Za-z1-9_-]+$ |
手机号码 | ^1[3|4|5|8][0-9]\d{8}$ |
URL | ^((http|https)://)?([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ |
18位身份证号 | ^(\d{6})(18|19|20)?(\d{2})([01]\d)([0123]\d)(\d{3})(\d|X|x)?$ |