0%

正则表达式

正则表达式

元字符

  1. \w 匹配一个字符,可以是数字、下划线、字母或者汉字
  2. \b 匹配的是位置,它的前一个字符和后一个字符不全是(一个是, 一个不是或不存在) \w
  • \bhi\b 匹配hi单词, 对于history和him都不匹配
  • \bhi\b.*\blucy\b hi单词后面若干字符之后是lucy
  1. . 匹配除了换行符之外的任一字符
    \bhi.lucy\b在hi和lucy中存在一个除了换行符之外的任一字符
  2. * 匹配的是数量 0~若干个
    \bhi\b.*\blucy\b hi单词后面若干个除换行符以外的字符之后是lucy
  3. \s 匹配任意的空白符
  4. \d 匹配数字
    0\d{2}-\d{8} 匹配以0开头,紧接着是2位数字,接着是-,最后是8位数字
    \d+ 匹配多于一个数字
  5. [0123456789]或者[0-9] 匹配数字与\d的意义完全相同
  6. ^ 匹配字符串的开始
  7. $ 匹配字符串的结束
    ^\b{5,12}$ 匹配5到12位的数字

字符转义

使用斜杠\对特殊字符转义,转换为普通字符

重复

代码/语法 说明
* 重复零次或更多次
+ 重复一次或者更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

windows\d+ windows后面紧跟着多于一个数字
^\w+ 以多于一个字符开头

字符类

  1. [aeiou] aeiou中的一个字符

  2. [0-9] 从0到9

  3. [0-9A-Za-z] 完全等同于 \w(如果不考虑中文字符)

  4. [) -] 从右括号、空格和减号中选择一个

    \(?0\d{2}[) -]?\d{8} 首先是一个转义字符\(, 它可能出现0次或1次(?), 然后是一个0, 后面跟着2个数字\d{2},然后是)或-或空格中的一个,它出现1此或不出现(?), 最后是8个数字(\d{8}).

分枝条件

分枝条件: 满足若干条件中的一个就能匹配. 要注意优先级和各条件顺序问题.

** 例1 匹配电话号码
要匹配两种电话号码形式: (0755)12345678 或者 0755-12345678 以及 0751-1234567

\(\d{3,4}\)\d{7,8}|\d{3,4}-\d{7,8}

** 例2: 注意分枝条件各条件的顺序
美国的邮编是有两种形式的: 5位数字 或者用连字号间隔的9位数字, 如12345和12345-1234两种形式.

应该使用\d{5}-\d{4}|\d{5}, 而不能使用\d{5}|\d{5}-\d{4}
也就是说, 如果\d{5}提前匹配了12345之后会返回而丢弃掉后面的四位.

分组

分组可以实现子表达式重复出现.

(\d{1,3}\.){3}\d{1,3} 匹配以点号分割的四组数字, 每组数字是1到3位,如 999.999.999.9991.1.0.0

** IP地址的匹配 **
IP地址分为4段, 每段的数字范围为: 0~255
正则表达式只能匹配字符串的格式, 不支持数值比较, 因此需要用尽可能精简的方式准确列出所有的可能性

((2[0-4]\d|25[0-5]|[0-1]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[0-1]?\d\d?)

解释:

  1. 2[0-4]\d 首位是2,第二位可以为0到4,第三位是任意数字
  2. 25[0-5] 前两位是25,第三位必须是0到5, 因为最大是255
  3. [0-1]?\d\d? 首位是0或1, 也可以首位不存在, 第二位为任何数字, 第三位是任意数字, 也可以不存在, 不存在这种情况下, 就只有前两位.
    为什么不是[0-1]?\d?\d?

反义

对字符类进行反义, 只需要将上面字符类一节的所有的字符转为大写

代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

后向引用

用于引用上文中匹配到的字符- 给上文中出现的表达式添加组号, 在下文中引用这个组号

代码/语法 说明
(exp) 匹配exp,并捕获文本到自动命名的组里
(?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号

分组命名规则为:

  • 分组0对应整个正则表达式
  • 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
  • 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

例1 自动命名
\b(\w+)\b\s+\1\b 首先匹配一个单词(\w+)并放到分组1中, 在下文中通过\1引用上文中分组1匹配到的字符.
该表达式用于匹配重复单词. 如 hello hello

例2 手动命名
\b(?<name>\w+)\b\s+\k<name>\b 首先匹配一个单词(\w+) 并将这个单词放入name组中, 在下文中引用name分组, 看后面是否再出现
注意反向引用时的格式: \k<name>

例3 不捕获
\b(?:\w+)\b\s+ 不捕获

零宽断言

零宽断言和负向零宽断言用于指示位置, 零宽断言分为两种, 分别指示匹配表达式的前面和后面.

零宽度正预测先行断言

用于指示匹配表达式的字符串的上一个字符位置

\b\w+(?=ing\b) 匹配以ing结尾的单词的前面部分(除了ing以外的部分).
如果查找 I'm singing while you're dancing.时, 它会匹配sing和danc.

零宽度正回顾后发断言

用于指示匹配表达式的字符串的下一个字符位置

(?<=\bre)\w+\b 匹配以re开头的单词的后半部分

((?<=\d)\d{3}+\b)匹配一个数字后面是若干的三个数字的后面部分.
对1234567890进行查找时结果是234567890

负向零宽断言

负向零宽断言, 与上一节中的正向零宽断言相对, 用于匹配不是某个字符或不在某些字符类里的方法

确保某个字符没有出现, 但并不想去匹配它, 就可以使用负向零宽断言

零宽度负预测先行断言(?!exp)

断言此位置的后面不能匹配表达式exp。
例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;
\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

零宽度负回顾后发断言

同理,可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:
(?<![a-z])\d{7} 匹配前面不是小写字母的七位数字。

请详细分析表达式(?<=<(\w+)>).*(?=<\/\1>),这个表达式最能表现零宽断言的真正用途。

一个更复杂的例子:
(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。
(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是),
然后是.*(任意的字符串),最后是一个后缀(?=</\1>)。注意后缀里的/,
它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,
前面的(\w+)匹配的内容,这样如果前缀实际上是的话,后缀就是了。
整个表达式匹配的是之间的内容(再次提醒,不包括前缀和后缀本身)。

注释

通过语法(?#comment)来包含注释

1
2
3
4
5
6
7
(?<=    # 断言要匹配的文本的前缀
<(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签)
) # 前缀结束
.* # 匹配任意文本
(?= # 断言要匹配的文本的后缀
<\/\1> # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
) # 后缀结束

贪婪与懒惰

代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

处理选项

名称 说明
IgnoreCase(忽略大小写) 匹配时不区分大小写。
Multiline(多行模式) 更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)
Singleline(单行模式) 更改.的含义,使它与每一个字符匹配(包括换行符\n)。
IgnorePatternWhitespace(忽略空白) 忽略表达式中的非转义空白并启用由#标记的注释。
ExplicitCapture(显式捕获) 仅捕获已被显式命名的组。

平衡组/递归匹配

用于在嵌套的层次结构中, 判断是否嵌套.

  • (?'group') 把捕获的内容命名为group,并压入堆栈(Stack)
  • (?'-group') 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败
  • (?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
  • (?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<                         #最外层的左括号
[^<>]* #最外层的左括号后面的不是括号的内容
(
(
(?'Open'<) #碰到了左括号,在黑板上写一个"Open"
[^<>]* #匹配左括号后面的不是括号的内容
)+
(
(?'-Open'>) #碰到了右括号,擦掉一个"Open"
[^<>]* #匹配右括号后面不是括号的内容
)+
)*
(?(Open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果还有,则匹配失败

> #最外层的右括号

我们需要做的是每碰到了左括号,就在压入一个”Open”,每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。

其他语法

代码/语法 说明
\a 报警字符(打印它的效果是电脑嘀一声)
\b 通常是单词分界位置,但如果在字符类里使用代表退格
\t 制表符,Tab
\r 回车
\v 竖向制表符
\f 换页符
\n 换行符
\e Escape
\0nn ASCII代码中八进制代码为nn的字符
\xnn ASCII代码中十六进制代码为nn的字符
\unnnn Unicode代码中十六进制代码为nnnn的字符
\cN ASCII控制字符。比如\cC代表Ctrl+C
\A 字符串开头(类似^,但不受处理多行选项的影响)
\Z 字符串结尾或行尾(不受处理多行选项的影响)
\z 字符串结尾(类似$,但不受处理多行选项的影响)
\G 当前搜索的开头
\p{name} Unicode中命名为name的字符类,例如\p{IsGreek}
(?>exp) 贪婪子表达式
(?-exp) 平衡组
(?im-nsx:exp) 在子表达式exp中改变处理选项
(?im-nsx) 为表达式后面的部分改变处理选项
(?(exp)yes no)
(?(exp)yes) 同上,只是使用空表达式作为no
(?(name)yes no)
(?(name)yes) 同上,只是使用空表达式作为no

常用的正则表达式

电子邮箱Email

\w+([-+.]\w+)*@\w+([-.]\w)*\.\w+([-.]\w+)*
Email地址是以@分割, @前面的部分为用户名, 后面为域名.
用户名部分可以包含字母/数字/下划线/-/+/., 但是只能字母/数字/下划线开头.
不能出现+-.连续
而域名部分可以包含字母/数字/下划线/-以及. 也是只能字母/数字/下划线开头.

用户名部分\w+([-+.]\w+)* , 必须是至少一个\w开头,

手机号码

[1-9][3,4,5,7,8]/d{9}

汉字

[\u4e00-\u9fa5]

QQ号码

[1-9]\d{4,}

邮编

[1-9]\d{5}

不含abc的单词

\b((?!abc)\w)+\b

时间(小时:分钟, 24小时制)

((1|0?)[0-9]|2[0-3]):([0-5][0-9])

姓名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String regex="^([(?<cc>a-zA-Z\\u4e00-\\u9fa5)]+)|([a-zA-Z\\u4e00-\\u9fa5]+[a-zA-Z\\u4e00-\\u9fa5. _·]*)$";
String [] tem={
"张大千",
"张.大千",
"zhangdaqian",
"zhang.daqian",
"张大千Quene",
"zhang daqian",
"zhang_daqian",
" 张大千",
".张大千",
"_张大千"
};
for (String s : tem) {
final boolean matches = Pattern.matches(regex, s);
System.out.println(s+":\t"+(matches?"√":"×"));
}
1
2
3
4
5
6
7
8
9
10
张大千:	√
张.大千: √
zhangdaqian: √
zhang.daqian: √
张大千Quene: √
zhang daqian: √
zhang_daqian: √
张大千: ×
.张大千: ×
_张大千: ×

Java中运用

java.util.regex 包主要由三个类所组成:Pattern、Matcher 和 PatternSyntaxException。

  • Pattern 对象表示一个已编译的正则表达式。Pattern 类没有提供公共的构造方法。要构建一个模式,首先必须调用公共的静态 compile 方法,它将返回一个 Pattern 对象。这个方法接受正则表达式作为第一个参数。
  • Matcher 是一个靠着输入的字符串来解析这个模式和完成匹配操作的对象。与 Pattern 相似,Matcher 也没有定义公共的构造方法,需要通过调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
  • PatternSyntaxException 对象是一个未检查异常,指示了正则表达式中的一个语法错误。

Pattern

1
2
3
4
5
6
7
8
String message="";
// 大小写不敏感
Pattern pattern = Pattern.compile(message, Pattern.CASE_INSENSITIVE );
// 大小写不敏感且使用Unix的行结束符
pattern = Pattern.compile("[az]$", Pattern.MULTILINE | Pattern.UNIX_LINES);
// 使用int变量, 启用Unicode折叠感知和大小写不敏感
final int flags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
Pattern pattern = Pattern.compile("aa", flags);

** matches(String, CharSequence) 方法 **

Pattern 类定义了一个方便的 matches 方法,用于快速地检查模式是否表示给定的输入字符串。与使用所有的公共静态方法一样,应该通过它的类名来调用 matches 方法,诸如 Pattern.matches(“\d”,”1”);。这个例子中,方法返回 true,这是由于数字“1”匹配了正则表达式\d。

** split(String) 方法 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
String REGEX = ":";
String INPUT = "one:two:three:four:five";
Pattern p = Pattern.compile(REGEX);
String[] items = p.split(INPUT);
for(String s : items) {
System.out.println(s);
}
REGEX="//d";
INPUT="one9two4three7four1five";
Pattern p = Pattern.compile(REGEX);
String[] items = p.split(INPUT);
for(String s : items) {
System.out.println(s);
}
// 输出结果 one two three four five

** public static String quote(String s)** :返回指定字符串字面模式的字符串。此方法会产生一个字符串,能被用于构建一个与字符串 s 匹配的 Pattern,好像它是一个字面上的模式。输入序列中的元字符和转义序列将没有特殊的意义了。
**  public String toString() ** :返回这个模式的字符串表现形式。这是一个编译过的模式中的正则表达式。

** java.lang.String中等价的方法 **

java.lang.String 通过模拟 java.util.regex.Pattern 行为的几个方法,也可以支持正则表达式。方便起见,下面主要摘录了出现在 API 关键的方法。

  • public boolean matches(String regex):告知字符串是否匹配给定的正则表达式。调用 str.matches(regex)方法所产生的结果与作为表达式的 Pattern.matches(regex, str)的结果是完全一致。
  • public String[] split(String regex, int limit):依照匹配给定的正则表达式来拆分字符串。调用 str.split(regex, n)方法所产生的结果与作为表达式的 Pattern.compile(regex).split(str, n) 的结果完全一致。
  • public String[] split(String regex):依照匹配给定的正则表达式来拆分字符串。这个方法与调用两个参数的 split 方法是相同的,第一个参数使用给定的表达式,第二个参数限制为 0。在结果数组中不包括尾部的空字符串。
  • 还有一个替换方法,把一个 CharSequence 替换成另外一个:
    public String replace(CharSequence target,CharSequence replacement):将字符串中每一个匹配替换匹配字面目标序列的子字符串,替换成指定的字面替换序列。这个替换从字符串的开始处理直至结束,例如,把字符串“aaa”中的“aa”替换成“b”,结果是“ba”,而不是“ab”。

Matcher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

索引方法

  索引方法(index methods)提供了一些正好在输入字符串中发现匹配的索引值:
  public int start():返回之前匹配的开始索引。
  public int start(int group):返回之前匹配操作中通过给定组所捕获序列的开始索引。
  public int end(): 返回最后匹配字符后的偏移量。
public int end(int group): 返回之前匹配操作中通过给定组所捕获序列的最后字符之后的偏移量。

研究方法

  研究方法(study methods)回顾输入的字符串,并且返回一个用于指示是否找到模式的布尔值。
  public boolean lookingAt(): 尝试从区域开头处开始,输入序列与该模式匹配。
  public boolean find(): 尝试地寻找输入序列中,匹配模式的下一个子序列。
  public boolean find(int start): 重置匹配器,然后从指定的索引处开始,尝试地寻找输入序列中,匹配模式的下一个子序列。
  public boolean matches(): 尝试将整个区域与模式进行匹配

替换方法

  替换方法(replacement methods)用于在输入的字符串中替换文本有用处的方法。
  public Matcher appendReplacement(StringBuffer sb, String replacement):实现非结尾处的增加和替换操作。
  public StringBuffer appendTail(StringBuffer sb):实现结尾处的增加和替换操作。
  public String replaceAll(String replacement):使用给定的替换字符串来替换输入序列中匹配模式的每一个子序列。
  public String replaceFirst(String replacement):使用给定的替换字符串来替换输入序列中匹配模式的第一个子序列。
  public static String quoteReplacement(String s):返回指定字符串的字面值来替换字符串。这个方法会生成一个字符串,用作 Matcher 的 appendReplacement 方法中的字面值替换 s。所产生的字符串将与作为字面值序列的 s 中的字符序列匹配。斜线(\)和美元符号($)将不再有特殊意义了。

** 使用 start 和 end 方法 **

1
2
3
4
5
6
7
8
9
10
11
String REGEX = "\\bdog\\b";
String INPUT = "dog dog dog doggie dogg";
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // 获得匹配器对象
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());
}

** 使用 matches 和 lookingAt 方法 **

1
2
3
4
5
6
7
8
9
10
11
String REGEX = "foo";
String INPUT = "fooooooooooooooooo";
Pattern pattern;
Matcher matcher;
// 初始化
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());

** 使用 replaceFirst(String) 和 replaceAll(String) 方法 **

1
2
3
4
5
6
7
8
String REGEX = "dog";
String INPUT = "The dog says meow. All dogs say meow.";
String REPLACE = "cat";

Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // 获得匹配器对象
INPUT = m.replaceAll(REPLACE);
System.out.println(INPUT);

** 使用 appendReplacement(StringBuffer, String) 和
  appendTail(StringBuffer) 方法 **

1
2
3
4
5
6
7
8
9
10
11
String REGEX = "a*b";
static String INPUT = "aabfooaabfooabfoob";
static String REPLACE = "-";
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // 获得匹配器对象
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, REPLACE);
}
m.appendTail(sb);
System.out.println(sb.toString());

** 在 java.lang.String 中等价的 Matcher 方法 **

为了使用方便,String 类看上去还不错地模仿了 Matcher 的两个方法:

public String replaceFirst(String regex, String replacement):使用给定的替换字符串替换该字符串中匹配了给定正则表达式的第一个子字符串。调用 str.replaceFirst(regex, repl)方法与使用 Pattern.compile(regex).matcher(str).replaceFirst(repl)产生的结果是完全相同的。

public String replaceAll(String regex, String replacement):使用给定的替换字符串替换该字符串中匹配了给定正则表达式的每一个子字符串。调用 str.replaceAll(regex, repl)方法与使用 Pattern.compile(regex).matcher(str).replaceAll(repl)产生的结果是完全相同的。

PatternSyntaxException

PatternSyntaxException 是未检查异常,指示正则表达式模式中的语法错误。PatternSyntaxException 类提供了下面的一些方法,用于确定在什么地方发生了错误:

  • public String getDescription():获得错误描述。
  • public int getIndex():获得错误索引。
  • public String getPattern():获得字符串形式的错误正则表达式。
  • public String getMessage():获得一个多行的字符串,包括语法错误和错误的索引、错误的正则表达式模式,以及模式内可视化的索引指示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Pattern pattern = null;
Matcher matcher = null;

Console console = System.console();
if (console == null) {
System.err.println("No console.");
System.exit(1);
}
while (true) {
try {
pattern = Pattern.compile(console.readLine("%nEnter your regex: "));
matcher = pattern.matcher(console.readLine("Enter input string to search: "));
} catch (PatternSyntaxException pse){
console.format("There is a problem with the regular expression!%n");
console.format("The pattern in question is: %s%n", pse.getPattern());
console.format("The description is: %s%n", pse.getDescription());
console.format("The message is: %s%n", pse.getMessage());
console.format("The index is: %s%n", pse.getIndex());
System.exit(0);
}
boolean found = false;
while (matcher.find()) {
console.format("I found the text \"%s\" starting at " +
"index %d and ending at index %d.%n",
matcher.group(), matcher.start(), matcher.end()
);
found = true;
}
if (!found){
console.format("No match found.%n");
}
}
}

执行结果

1
2
3
4
5
6
7
8
Enter your regex: ?i)
There is a problem with the regular expression!
The pattern in question is: ?i)
The description is: Dangling meta character '?'
The message is: Dangling meta character '?' near index 0
?i)
^
The index is: 0

问题与练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
〖问题〗

1. 在 java.util.regex 包中有哪三个公共的类?描述一下它们的作用。
2. 考虑一下字符串“foo”,它的开始索引是多少?结束索引是多少?解释一下这些编号的意思。
3. 普通字符和元字符有什么不同?各给出它们的一个例子。
4. 如何把元字符表现成像普通字符那样?
5. 附有方括号的字符集称为什么?它有什么作用?
6. 这里是三个预定义的字符类:\d、\s和\w。描述一下它们各表示什么?并使用方括号的形式将它们重写。
7. 对于\d、\s和\w,写出两个简单的表达式,匹配它们相反的字符集。
8. 思考正则表达式(dog){3},识别一下其中的两个子表达式。这个表达式会匹配什么字符串?

〖练习〗

1. 使用反向引用写一个表达式,用于匹配一个人的名字,假设这个人的 first 名字与 last 名字是相同的。

【问题答案】

1. 问:在 java.util.regex 包中有哪三个公共的类?描述一下它们的作用。
答:

编译后的 Pattern 实例表示正则表达式。
Matcher 实例是解析模式和靠着输入的字符串完成匹配操作的引擎。
PatternSyntaxException 定义一个未检查异常,指示正则表达式中的语法错误。

2. 问:考虑一下字符串“foo”,它的开始索引是多少?结束索引是多少?解释一下这些编号的意思。

答:字符串中的每一个字符位于其自身的单元格中。索引位置在两个单元格之间。字符串“foo”开始于索引 0,结束于索引 3,即便是这些字符仅占用了 0、1 和 2 号单元格。

3. 问:普通字符和元字符有什么不同?各给出它们的一个例子。

答:正则表达式中的普通字符匹配其本身。元字符是一个特殊的字符,会影响被匹配模式的方式。字母A是一个普通字符。标点符号.是一个元字符,其匹配任意的单字符。

4. 问:如何把元字符表现成像普通字符那样?答:有两种方法:

在元字符前加上反斜线(\);
把元字符置于\Q(开始)\E(结束)的引用表达式中。

5. 问:附有方括号的字符集称为什么?它有什么作用?

答:是一个字符类。通过方括号间的表达式,匹配指定字符类中的任意一个字符。

6. 问:这里是三个预定义的字符类:\d、\s和\w。描述一下它们各表示什么?并使用方括号的形式将它们重写。

答:\d 匹配任意数字[0-9]
  \s 匹配任意空白字符[ \t\n-x0B\f\r ]
  \w 匹配任意单词字符[a-zA-Z_0-9]

7. 问:对于\d、\s和\w,写出两个简单的表达式,匹配它们相反的字符集。

答:\d \D [^\d]
  \s \S [^\s]
  \w \W [^\w]

8. 问:思考正则表达式(dog){3},识别一下其中的两个子表达式。这个表达式会匹配什么字符串?

答:表达式由捕获组(dog)和接着的贪婪量词{3}所组成。它匹配字符串“dogdogdog”。

【练习答案】

1. 练习:使用反向引用写一个表达式,用于匹配一个人的名字,假设这个人的 first 名字与 last 名字是相同的。

解答:([A-Z][a-zA-Z]*)\s\1

使用

替换字符串中的字符串

1
2
3
4
5
6
7
8
String regularExpressionString=...;
Matcher m = Pattern.compile(regularExpressionString, Pattern.CASE_INSENSITIVE).matcher(source);
String result=m.replaceAll(newstring);
System.out.println("使用正则表达式不区分大小写的替换结果"+result);

Matcher m1 = Pattern.compile(regularExpressionString, Pattern.CANON_EQ).matcher(source);
String result1=m1.replaceAll(newstring);
System.out.println("使用正则表达式区分大小写的替换结果"+result1);

参考文献:

  1. 正则表达式30分钟入门教程
  2. java正则表达式语法详解及其使用代码实例