正则表达式是一种强大而灵活的文本处理工具。使用正则表达式,我们能够以编程的方式,构造复杂的文本模式,并对输入的字符串进行搜索。
1 基础
一般来说,正则表达式就是以某种方式来描述字符串。
在正则表达式中,用\d表示一位数字。由于Java对反斜线的不同处理,在其它语言中,\表示想要在正则表达式中插入一个普通的反斜线,请不要给他任何特殊的意义。而在Java中,\的意思是我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。例如,如果想表示一位数字,那么正则表达式应该是\d。如果你想插入一个普通的反斜线,则应该这样\\。不过换行和制表符之类的东西只需使用单反斜线:\n\t。
构建正则表达式
字符 | 说明 |
---|---|
\ | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,”n”匹配字符”n”。”\n”匹配换行符。序列”\\”匹配”\”,”\(“匹配”(“。 |
? | 零次或一次匹配前面的字符和子表达式 |
+ | 一次或多次匹配前面的字符和子表达式 |
* | 零次或多次匹配前面的字符和子表达式 |
. | 匹配除”\r\n”之外的任何单个字符 |
B | 指定字符B |
\xhh | 十六进制为oxhh的字符 |
\uhhhh | 十六进制表示为oxhhhh的Unicode字符 |
\t | 制表符Tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\e | 转义(Escape) |
例如,表示”可能有一个负号,后面跟着一位或多位数字“,可以这样:-?\d+
public class IntegerMatch{
public static void main(String[] args){
System.out.println("-1234".matches("-?\\d+"));
System.out.println("4568".matches("-?\\d+"));
System.out.println("+4568".matches("-?\\d+"));
//字符+在正则表达式中有特殊意义,所以必须使用\\转义
//在正则表达式中,括号有着将表达式分组的效果,竖直线|则表示或操作。
System.out.println("+4568".matches("(-|\\+)?\\d+"));
}
}
/*
运行结果为(做的是一个完全匹配):
true
true
false
true
*/
字符类 | |
---|---|
任意字符 | |
[abc] | 包含a、b和c的任何字符(和a |
[^abc] | 除了a、b和c的任何字符(否定) |
[a-zA-Z] | 从a到z或从A到Z的任何字符(范围) |
[abc[hij]] | 任意a、b、c、h、i和j字符(合并) |
[a-z&&[hij] | 任意h、i和j(相交) |
\s | 空白符(空格、tab、换行、换页、回车) |
\S | 非空白符([^\s]) |
\d | 数字[0-9] |
\D | 非数字[^0-9] |
\w | 词字符[a-zA-Z0-9] |
\W | 非词字符[^\w] |
逻辑操作符 | |
---|---|
XY | Y跟在X后面 |
X | Y |
(X) | 捕获组。可以在表达式中用\i引用第i个捕获组 |
边界匹配符 | |
---|---|
^ | 一行的起始 |
$ | 一行的结束 |
\b | 词的边界 |
\B | 非词的边界 |
\G | 前一个匹配的结束 |
量词
量词描述了一个模式吸收输入文本的方式:
1. 贪婪型:贪婪表达式会为所有可能的模式发现尽可能多的匹配。假定我们的模式仅能匹配第一个可能的字符组,如果它是贪婪的,那么它就会继续往下匹配。
2. 勉强型:用问号来指定,这个量词匹配满足模式所需的最少字符数。
3. 占有型:这种类型的量词只在Java语言中才可用。用于防止正则表达式失控,因此可以是正则表达式执行起来更有效。
贪婪型 | 勉强型 | 占有型 | 如何匹配 |
---|---|---|---|
X? | X?? | X?+ | 一个或零个X |
X* | X*? | X*+ | 零个或多个X |
X+ | X+? | X++ | 一个或多个X |
X{n} | X{n}? | X{n}+ | 恰好n次X |
X{n,} | X{n,}? | X{n,}+ | 至少n次X |
X{n,m} | X{n,m}? | X{n,m}+ | X至少n次,且不超过m次 |
表达式X通常必须要用括号括起来。例如:
abc+ 匹配ab,后面跟随一个或多个c
(abc)+ 匹配一个或多个完整的abc字符串
下面是一个测试正则表达式的程序:
import java.util.regex.*;
public class TestRegex{
public static void main(String[] args){
if(args.length < 2){
pf("需要待测试的字符串 正则表达式");
System.exit(0);
}
pf("Input: \""+args[0]+"\"");
for(String arg : args){
pf("Regex: \""+arg+"\"");
Pattern p = Pattern.compile(arg);
Matcher m = p.matcher(args[0]);
//find()像迭代器那样前向遍历输入字符串
while(m.find()){
pf("Match \""+m.group()+"\" at positions "+
m.start()+" - "+(m.end()-1));
}
}
}
static void pf(String s){
System.out.println(s);
}
}
/*
Input: "ababababasba"
Regex: "ababababasba"
Match "ababababasba" at positions 0 - 11
Regex: "ab"
Match "ab" at positions 0 - 1
Match "ab" at positions 2 - 3
Match "ab" at positions 4 - 5
Match "ab" at positions 6 - 7
注意:通过参数传入的正则表达式含有\,
如\d,只需要写一个\即可,这和在程序中写\\d不同。
*/
组
组是用括号划分的正则表达式,可以根据组的编号来引用某个组。组号为0表示整个表达式,组号1表示被第一对括号括起的组,依次类推。
A(B(C))D
有三个组:组0是ABCD,组1是BC,组2是C。
import java.util.regex.*;
public class Groups{
public static final String POEM =
"Twas briling, 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"
;
public static void main(String[] args){
Matcher m =
Pattern.compile("(\\S+)\\s+((\\S+)\\s+)$").matcher(POEM);
System.out.println("整个匹配(组0)情况为:");
while(m.find()){
System.out.print("{"+m.group()+"}");
}
System.out.println();
m.reset(); //重置标记
while(m.find()){
System.out.println(m.groupCount());
for(int j=0;j<=m.groupCount();j++){
System.out.print("{ j="+j+" "+m.group(j)+"}");
}
System.out.println();
}
}
}
/*
运行结果为:
整个匹配(组0)情况为:
{raths outgrabe.
}
3
{ j=0 raths outgrabe.
}{ j=1 raths}{ j=2 outgrabe.
}{ j=3 outgrabe.}
分析:组是以括号表示的,
m.groupCount()返回该匹配器的模式中的分组数目,不包括组0(整个匹配)
首先第一次匹配全部,然后在其中依次匹配(\\S+)、((\\S+)\\s+)、(\\S+)
*/