正则表达式必知必会

最近花了一点时间看完《正则表达式必知必会》,记录下学习的知识点。正则表达式主要是用于处理文本,用于搜索和替换。

0x01. 匹配单个字符

(1) 匹配纯文本

1
2
3
4
#文本
Hello, my name is Ben. Please visit my website at http://www.forta.com
#正则表达式
Ben

(2)匹配任意字符 ( 使用元字符. )
(3)匹配特殊字符 (使用转义字符\ 转义元字符)

0x02. 匹配一组字符

(1)匹配多个字符中的某一个 ( 使用元字符[和],如[ns])
(2)利用字符集合区间 (使用元字符- ,如:[A-Za-z0-9] )
(3)取非匹配 ( 使用元字符^,如:[^0-9])

0x03. 使用元字符

(1) 对特殊字符进行转义 (使用转义字符)
(2) 匹配空白字符

1
2
3
4
5
6
7
8
| 元字符    |    说明    |
| ------- | :-------: |
| [\b] | 回退(并删除)一个字符(Backspace键)|
| \f | 换页符|
| \n | 换行符|
| \r | 回车符|
| \t | 制表符(Tab键)|
| \v | 垂直制表符|

(3) 匹配特定的字符类别
1)匹配数字(与非数字)

1
2
\d    匹配任何一个数字字符(等价于[0-9])
\D 匹配任何一个非数字字符(等价于[^0-9])

2)匹配字母和数字(与非字母和数字)

1
2
\w    匹配任何一个字母数字字符(大小写均可)或下划线(等价于[a-zA-Z0-9_])
\W 匹配任何一个非字母数字或下划线字符(等价于[^a-zA-Z0-9_])

3)匹配空白字符(与非空白字符)

1
2
\s    匹配任何一个空白字符(等价于[\f\n\r\t\v])
\S 匹配任何一个非空白字符(等价于[^\f\n\r\t\v])

(4) 使用POSIX字符类
POSIX字符类是许多(但不是所有)正则表达式实现都支持的一种简写形式。

0x04. 重复匹配

  1. 有多个匹配
    1) 匹配一个或多个字符(使用元字符 +
    2)匹配零个或多个字符(使用元字符 *
    3)匹配零个或一个字符(使用元字符 ?
  2. 匹配的重复次数
    1) 为重复匹配次数设定一个精确的值(如:{3}
    2) 为重复匹配次数设定一个区间(如:{3-9}
    3) 匹配“至少重复多少次”(如:{3, }
  3. 防止过度匹配
    在进行模式匹配的时候会出现一种过度匹配的现象:在文本中匹配 <[Bb]>.*</[Bb]>时,会匹配第一个到最后一个之间的所有字符。原因:*+都是所谓的“贪婪型”元字符,它们在进行匹配时的 行为模式是多多益善而不是适可而止。

解决的方法:使用元字符的”懒惰型”版本,即在元字符后面加一个?后缀。

1
2
3
4
贪婪型元字符         懒惰型元字符
* *?
+ +?
{n, } (n, )?

0x05. 位置匹配

(1) 单词边界 (使用元字符 \b表示,如:\bcat\b)

1
2
3
4
5
6
#文本
The caption wore his cap and cape proudly as he sat listening to the recap of how his crew saved the men from a capsized vessel.
#正则表达式 匹配结果
\bcap\b cap
\bcap captain cap cape capsized
cap\b cap recap

(2) 字符串边界(使用元字符^和$分别表示字符串的首和尾)

1
2
3
4
5
6
7
#文本
<?xml version="1.0" encoding="UTF-8" ?>
<wsdl:definitions targerNamespace="http://tips.cf"
xmlns:impl="http://tips/cf" xmlns:intf="http://tips.cf"
xmlns:apachesoap="http://xml.apache.org/xml.soap"
#正则表达式
^\s*<\?xml.*\?> 匹配第一行

0x06. 使用子表达式

(1) 子表达式 (使用元字符(和)包含表达式)
(2) 子表达式的嵌套

0x07. 回溯引用:前后一致匹配

(1) 回溯引用匹配

1
2
3
#正则表达式
[ ]+(\w+)[ ]+\1
<[hH](1-6)>.*?</[hH]\1> #.*?表示懒惰型.*匹配

(2) 回溯引用在替换操作中的应用

1
2
3
4
#正则表达式                            
(\w+[\w\.]*@[\w\.]+\.\w+)
#替换
<A HREF="mailto:$1">$1</A>

0x08. 前后查找

(1) 向前查找(以 ?= 开头的子表达式)

1
2
3
4
5
6
#文本
http://www.forta.com/
https://mail/forta.com/
ftp://ftp.forta.com/
#正则表达式 结果
.*(?=:) http https ftp #?=表示:只要找到:就行了,不要把它包括在最终的匹配结果里。

(2) 向后查找(以 ?<= 开头的子表达式)

1
2
3
4
5
6
7
8
#文本
ABC01: $23.45
HGG42: $5.31
CFMX1: $899.00
XTC99: $69.96
Total items found: 4
# 正则表达式 结果
(?<=\$)[0-9.]+ 23.45 5.31 899.00 69.96

(3) 对前后查找取非
负向前查找将向前查找不与给定模式匹配的文本,负向后查找将向后查找不与给定模式匹配的文本。

1
2
3
4
5
6
7
8
9
10
11
12
操作符          说明
(?=) 正向前查找
(?!) 负向前查找
(?<=) 正向后查找
(?<!) 负向后查找
#文本
I paid $30 for 100 apples, 50 oranges, and 60 pears.
I saved $5 on this order.

#正则表达式
(?<=\$)\d+ #向后查找价格
\b(?<!\$)\d+\b #只查找数量

0x09. 嵌入条件

正则表达式里的条件要用?定义。
?匹配前一个字符或表达式,如果它存在的话。
?=和?<=匹配前面或后面的文本,如果它存在的话。
嵌入条件语法也使用了?,嵌入条件的两种情况:1) 根据一个回溯引用来进行条件处理;2)根据一个前后查找来进行条件处理。
(1)回溯引用条件
定义这种条件的语法是:(?(backreference)true-regex|false-regex) ;也可以只用前面的true-regex;

1
2
3
4
5
6
7
8
9
10
11
12
#文本
123-456-7890
(123)456-7890
(123)-456-7890
(123-456-7890
1234567890
123 456 7890
#正则表达式 匹配美式电话号码
(\()?\d{3}(?(1)\)|-)\d{3}-\d{4})
#结果
123-456-7890
(123)456-7890

(2)前后查找条件
定义这种条件的语法类似上面,只需把回溯引用替换为一个完整的前后查找表达式就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
#文本  美国的邮政编码两种格式:12345形式的ZIP格式;12345-6789形式的ZIP+4格式
11111
22222
33333
44444-
55555-5555
#正则表达式
\d{5}(?(?=-)-\d{4}) #匹配美式邮编
#结果
11111
22222
33333
55555-5555