正则表达式基础
文章目录
定义
- 正则表达式 (Regular Expression) : 描述了一种字符串匹配的模式,可以用来:检查一个串中是否含有符合某个规则的子串,并且可以得到这个子串;根据匹配规则对字符串进行灵活的替换操作。
用途
-
正则表达式的主要应用对象是文本,可以在大多数的文本编辑器中使用。
- 替换文本。可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。
- 基于模式匹配从字符串中提取子字符串。可以查找文档内或输入域内特定的文本。
-
很多编程语言都支持使用正则表达式进行字符串操作。
- 比如:
python
、Java
、JavaScript
、Ruby
和C#
等编程语言都支持大部分正则表达式的规则。
- 比如:
-
测试字符串内的模式。
- 比如可以测试输入字符串,查看字符串内是否出现电话号码模式或信用卡号码模式。这也被称作是数据验证1。
正则表达式的用途十分广泛,不局限于上述的几个例子,还有很多应用场景可以使用正则表达式来达到事半功倍的效果!
语法规则
接下来就用以下模式来举例正则表达式的使用
匹配字符串
⇒
‘
正
则
表
达
式
‘
\stackrel{ `正则表达式`}{\Rightarrow}
⇒‘正则表达式‘ 匹配结果
例如 : 匹配字符串 : abcd
, 正则表达式为ab
, 匹配结果为 : ab
则写成 abcd
⇒
a
b
\stackrel{ab}{\Rightarrow}
⇒ab ab
为了方便,举例子统一用e.g. 2 该缩写代替
单个字符匹配
-
字符 : 字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是字符。表达式中的字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
- e.g.
a
,1
,哈
,_
,,
,'
等等
- e.g.
-
字符串 :有两个及两个以上的字符组成的符号串
- e.g.
abcdef
,你好
- e.g.
-
单个字符匹配的意思是 一个字符对应匹配一个字符
匹配简单字符
最简单的匹配规则 : 根据正则表达式中的字符进行挨个匹配, 顺序需要一致才能匹配成功
e.g.
abcdef
⇒ c d e \stackrel{cde}{\Rightarrow} ⇒cdecde
匹配成功,匹配的位置从2开始,结束于5 (这里的下标统一从0开始, 顾头不顾腚即从下标2开始取值,下标5并不取值)cdrf
⇒ a b c \stackrel{abc}{\Rightarrow} ⇒abc匹配失败
匹配失败,没有找到对应的子串
转义字符
一些不方便书写的字符,需要使用转义符\
来进行转义
\r
: 代表回车\n
: 代表换行, 也叫换行符\t
: 代表缩进, 也叫制表符\\
: 代表\
本身\^
: 代表^
本身\$
: 代表$
本身\.
: 代表.
小数点本身\*
: 代表*
号\?
: 代表?
号
因为有些特殊字符在正则表达式中有特殊的含义, 要匹配那些特殊字符就必须使用转义才能匹配的到
e.g.
ab^cde$f*t
⇒ \ ∧ c d e \stackrel{ \backslash \wedge cde}{\Rightarrow} ⇒\∧cde^cde
匹配成功,匹配的位置从2开始,结束于6 (这里的下标统一从0开始, 顾头不顾腚即从下标2开始取值,下标6并不取值)cdfff*cc*?
⇒ \ ∗ c c \ ∗ \ ? \stackrel{ \backslash* cc\backslash*\backslash?}{\Rightarrow} ⇒\∗cc\∗\?*cc*?
匹配成功,匹配的位置从5开始, 匹配到最后3.1415
⇒ 3 \ . 14 \stackrel{ 3\backslash.14}{\Rightarrow} ⇒3\.143.14
匹配成功,匹配的位置从0开始,结束于4
匹配一类字符
指将字符按类别进行划分,比如数字类的字符,字母类的字符等等;而匹配一类字符是指匹配该类别字符中的任意一个字符
e.g. \d
可以匹配任意一个数字
\d
: 代表任意一个数字; 0~9中的任意一个数字\w
: 代表任意一个字母或数字或下划线; 即A~Z, a~z, 0~9, _ 中的任意一个字符\s
: 代表空格、制表符、换页符和空白字符中的任意一个字符.
: 代表除了换行符(\n)3 以外的任意一个字符
e.g.
-
asd1314
⇒ \ d \ d \stackrel{ \backslash d\backslash d}{\Rightarrow} ⇒\d\d13
匹配成功,正则表达式的含义是匹配两个数字;匹配的位置从3开始,结束于5 -
accc10086d
⇒ c . \ d \stackrel{ c.\backslash d}{\Rightarrow} ⇒c.\dcc1
匹配成功,正则表达式的含义是匹配一个字符c和任意一个除了换行的字符和一个数字;匹配的位置从2开始,结束于5
自定义匹配多种字符
- 使用方括号
[ ]
包含的字符,能够匹配方括号内的任意一个字符。 - 用
[^ ]
包含的字符,则能够匹配除了方括号的字符之外的任意一个字符。 -
可以指定范围, 比如a-z
是指字母a到字母z这段范围
注意 : 只是匹配其中任意一个,而不是多个; 换句话来说就是一个方括号匹配一个字符
e.g. [asdf@ss]
匹配括号内的任意一个字符,大白话就是括号内的字符可以当做一个集合, 只要匹配的字符属于该集合即可匹配成功!
-
jcabc
⇒ [ c j ] \stackrel{[cj]}{\Rightarrow} ⇒[cj]j
匹配成功,正则表达式的含义是匹配属于方括号内的字符;匹配的位置从0开始,结束于1 -
ckiab5@
⇒ [ a b 2 @ ] \stackrel{[ab2@]}{\Rightarrow} ⇒[ab2@]a
匹配成功,正则表达式的含义是匹配属于方括号内的字符;匹配的位置从3开始,结束于4 -
adcjcd
⇒ [ j d c ] [ k c l ] \stackrel{[jdc][kcl]}{\Rightarrow} ⇒[jdc][kcl]dc
匹配成功,正则表达式的含义是匹配属于方括号内的字符;匹配的位置从1开始,结束于2 -
abcd
⇒ [ ∧ a b c ] \stackrel{[\wedge abc]}{\Rightarrow} ⇒[∧abc]d
**匹配成功,正则表达式的含义是匹配不属于方括号内的字符;匹配的位置从3开始,只匹配一个字符 ** -
abDcd
⇒ [ ∧ A − F 0 − 5 ] \stackrel{[\wedge A-F0-5]}{\Rightarrow} ⇒[∧A−F0−5]a
匹配成功,正则表达式的含义是匹配不属于方括号内的字符, 方括号内的含义是匹配除了A
到F
之间的大写字母,0
到5
的数字之外的字符 ;匹配的位置从0开始,只匹配一个字符 -
jdkdd
⇒ [ c − h ] \stackrel{[c-h]}{\Rightarrow} ⇒[c−h]d
匹配成功,正则表达式的含义是匹配属于方括号内的字符, 方括号内的含义是匹配c
到h
之间的小写字母的字符 ;匹配的位置从1开始,只匹配一个字符
多个字符匹配
重复的次数
放在被修饰的表达式
的后面。比如:[abcd][abcd]
这是匹配两个字符的正则表达式,可以写成[abcd]{2}
,这样当有多个相同规则的表达式时能方便书写!
这种可以多个字符匹配的表达式还有如下几种:
-
{n}
: 表达式重复n
次- e.g. 正则表达式
\w{3}
相当于\w\w\w
a{6}
相当于aaaaaa
\s{1}
相当于\s
- e.g. 正则表达式
-
{m,n}
: 表达式至少重复m
次, 最多重复n
次- e.g. 正则表达式
abc{1,2}
相当于匹配出abc
或abcc
- 注意 :
{}
的作用范围是前面的一个字符,而不是前面的所有字符
- e.g. 正则表达式
-
{m,}
: 表达式至少重复m
次, 没有至多的次数- e.g. 正则表达式
a\d{2,}
的含义是匹配有至少有两个数字以上的子串; 可以匹配出的有a12
,a111
,a1233
,a113344
, …,a098765...
可以有无数个 abc1234dda
⇒ [ b c \ d ] { 2 , } \stackrel{[bc\backslash d]\{2,\}}{\Rightarrow} ⇒[bc\d]{2,}bc1234
匹配成功,匹配位置从1开始,结束于7
- e.g. 正则表达式
-
?
: 匹配0次或1次,相当于{0,1}- e.g. 正则表达式
ac?
可以匹配到的有a
,ac
aabbcc
⇒ a [ c d ] ? \stackrel{a[cd]?}{\Rightarrow} ⇒a[cd]?ab
匹配成功,匹配位置从1开始,结束于3
- e.g. 正则表达式
-
+
: 表达式至少出现1次, 相当于{1,}- e.g. 正则表达式
a+c
可以匹配出ac
,aac
,aaac
,… aabbcc
⇒ a + [ c d ] ? \stackrel{a+[cd]?}{\Rightarrow} ⇒a+[cd]?aab
匹配成功,匹配位置从0开始,结束于3
- e.g. 正则表达式
-
*
: 表达式可以不出现或者出现任意次数, 相当于{0, }- e.g. 正则表达式
abc*b
可以匹配出abb
,abcb
,abccb
,abccccb
,… aabbccd
⇒ a [ c b ] ∗ d \stackrel{a[cb]*d}{\Rightarrow} ⇒a[cb]∗dabbccd
匹配成功,匹配位置从1开始,匹配到结尾
- e.g. 正则表达式
抽象意义的特殊符号
这些特殊符号并不代表任何一个字符, 主要是做字符串的定位使用, 并不充当任何字符的"角色"
-
^
: 与字符串开始的地方开始匹配, 不匹配任何字符 -
$
: 与字符串结束的地方匹配, 不匹配任何字符 -
\b
: 匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符
e.g. 正则表达式 ^aaa
匹配要求从头开始为a
-
abcd
⇒ ∧ a b c \stackrel{\wedge abc}{\Rightarrow} ⇒∧abcabc
匹配成功,正则表达式的含义是匹配a
在开头的字符;匹配的位置从0开始,结束于3 -
cccabcdccc
⇒ ∧ a b c \stackrel{\wedge abc}{\Rightarrow} ⇒∧abc 匹配失败, 因为a
不在开头 -
a b c d ⇒ a b c $ 匹 配 失 败 abcd\stackrel{abc\$}{\Rightarrow}匹配失败 abcd⇒abc$匹配失败匹配失败, 因为c不在结尾处
&&&avd
⇒ . \ b . \stackrel{.\backslash b.}{\Rightarrow} ⇒.\b.&a
匹配成功,正则表达式的含义是匹配不同类型的边界字符;匹配的位置从2开始,结束于4@@@@1234
⇒ . \ b . \stackrel{.\backslash b.}{\Rightarrow} ⇒.\b.@1
匹配成功,正则表达式的含义是正则表达式的含义是匹配不同类型的边界字符;匹配的位置从3开始,结束于5yesterday, Friday, day, Sunday
⇒ \ b d a y \ b \stackrel{\backslash bday \backslash b}{\Rightarrow} ⇒\bday\bday
匹配成功,正则表达式的含义是匹配出只有day的字符串
表达式内部的子表达式
下面的符号会影响表达式内部的子表达式之间的关系
|
: 左右两边表达式之间 “或” 关系,匹配左边或者右边()
: 这里可以分成两种含义-
- 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰; 即可以操作字符串进行次数匹配。而没带括号
()
只能操作单个字符 - 取匹配结果的时候,括号中的表达式匹配到的内容可以被单独得到, 即可以把括号内的多个字符作为字符串进行匹配; 它与中括号
[]
不同之处在于中括号只匹配一个字符, 而()
可以匹配多个字符
- 在被修饰匹配次数的时候,括号中的表达式可以作为整体被修饰; 即可以操作字符串进行次数匹配。而没带括号
-
e.g. 表达式1 | 表达式2
正则表达式的含义是匹配表达式1匹配表达式2, 只要符合即可匹配
-
I am zeny, he is Mike
⇒ z e n y ∣ M i k e \stackrel{zeny|Mike}{\Rightarrow} ⇒zeny∣Mikezeny
匹配成功,位置从5开始到9 -
la la la la ha ha ha ha
⇒ ( h a \ s ∗ ) + \stackrel{(ha\backslash s*)+}{\Rightarrow} ⇒(ha\s∗)+ha ha ha
匹配成功,表达式含义是匹配字符串ha
加空格, 因为最后的ha
没有带空格, 所以不匹配。从中可以看出*
是操作整个括号内的字符, 而不仅仅是*
号前面的单个字符 -
$12.2,¥10.1
⇒ ¥ ( \ d + \ . ? \ d ∗ ) \stackrel{¥(\backslash d +\backslash .?\backslash d*)}{\Rightarrow} ⇒¥(\d+\.?\d∗)¥10.1
匹配成功, 单独获取括号范围内匹配的结果, 与方括号相比,()
可以匹配更多字符
高级规则
贪婪匹配
贪婪匹配是指在重复匹配的过程中不指定匹配次数时, 将会尽可能多的匹配字符。
e.g. *
, {m,n}
, {m,}
, +
, ?
这些没有确定的匹配次数的特殊符号, 他们将会尽可能多的匹配字符。其中?
在匹配时匹配次数可以为0也可以为1, 但在匹配的过程中也是尽可能的要匹配, 这就是"贪婪"。
bbbaaabbbaa
⇒ ( a ) ( \ w + ) \stackrel{(a)(\backslash w +)}{\Rightarrow} ⇒(a)(\w+)aaabbbaa
匹配成功, 正则表达式的含义是匹配a
之后的所有字符, 默认是贪婪匹配的原则,就尽可能的匹配更多字符baccccccccbb
⇒ ( a ) ( \ w + ) ( b ) \stackrel{(a)(\backslash w +)(b)}{\Rightarrow} ⇒(a)(\w+)(b)accccccccbb
匹配成功, 正则表达式的含义是匹配a
之后的所有字符, 默认是贪婪匹配的原则,就尽可能的匹配更多字符,然后直到匹配最后的b
非贪婪匹配
一般的正则表达式表达式都是默认贪婪匹配。非贪婪匹配是指在重复匹配的过程中不指定匹配次数时, 将会尽可能少的匹配字符。只需要在修饰匹配次数的特殊符号后加上?
即可。
e.g. *
, {m,n}
, {m,}
, +
, ?
在这些没有确定的匹配次数的特殊符号后面加上?
即可非贪婪匹配, 他们将会尽可能少的匹配字符。
bbbabbb
⇒ ( a ) ( \ w + ? ) \stackrel{(a)(\backslash w +?)}{\Rightarrow} ⇒(a)(\w+?)ab
匹配成功, 正则表达式的含义是匹配a
之后的第一个字符, 非贪婪匹配的原则,就尽可能的匹配更少的字符bbbabbba
⇒ ( a ) ( \ w + ? ) ( a ) \stackrel{(a)(\backslash w +?)(a)}{\Rightarrow} ⇒(a)(\w+?)(a)abbba
匹配成功, 正则表达式的含义是匹配a
之后的字符, 非贪婪匹配的原则,因为要匹配到后面的a
, 就尽可能的匹配更少的字符直到字符a
再来举个贪婪匹配和非贪婪匹配区别的例子
<td><p>aa</p></td><td><p>bb</p></td>
⇒ ( < t d > ( . ∗ ) < / t d > ) ( a ) \stackrel{(<td>(.*)</td>)(a)}{\Rightarrow} ⇒(<td>(.∗)</td>)(a)<td><p>aa</p></td><td><p>bb</p></td>
<td><p>aa</p></td><td><p>bb</p></td>
⇒ ( < t d > ( . ∗ ? ) < / t d > ) ( a ) \stackrel{(<td>(.*?)</td>)(a)}{\Rightarrow} ⇒(<td>(.∗?)</td>)(a)<td><p>aa</p></td>
它们最直接的区别是贪婪匹配要匹配到更多的</td>
而非贪婪匹配只匹配到第一个</td>
即可
反向引用
- 表达式后边的部分,可以引用前面 “括号内的子匹配已经匹配到的字符串”。这也叫
反向引用
。- 引用方法是 “” 加上一个数字。
\1
引用第 1 对括号内匹配到的字符串,\2
引用第 2 对括号内匹配到的字符串……以此类推。 - 如果一对括号内包含另一对括号,则外层的括号先排序号。大白话就是哪一对的左括号 “(” 在最前面,那这一对就先排序号。
- 引用方法是 “” 加上一个数字。
e.g."Hello", 'hi'
⇒
(
"
∣
′
)
(
.
∗
?
)
(
\
1
)
)
(
a
)
\stackrel{( " |')(.*?)(\backslash 1))(a)}{\Rightarrow}
⇒("∣′)(.∗?)(\1))(a) "Hello"
'hi'
匹配成功, 正则表达式的含义是匹配"
或者'
之间的内容; (\1)是指重复第一个括号的规则, 匹配到两段字符串。
e.g. aa bbbb abcdefg ccccc 111121111
⇒
(
\
w
)
(
\
1
)
{
4
,
}
\stackrel{(\backslash w)(\backslash 1)\{4,\}}{\Rightarrow}
⇒(\w)(\1){4,} ccccc
匹配成功, 正则表达式的含义是匹配至少连续5次相同的字符; (\w)
指匹配到的任意字符, (\1)是指重复第一个括号匹配到的内容,而{4,}
则是匹配(\1)
的内容至少4次以上; 因此就会匹配到至少5次相同的字符。
预搜索与反向搜索
正向预搜索 : 格式有(?=xxxxx)
, (?!xxxxx)
; 在被匹配的字符串中,它对所处的 “缝隙” 或者 “两头” 附加的条件
是:所在缝隙的右侧,必须能够匹配上 xxxxx 这部分的表达式。
e.g. Windows 10 Windows XP
⇒
W
i
n
d
o
w
s
(
?
=
N
T
∣
X
P
)
\stackrel{Windows (?=NT|XP)}{\Rightarrow}
⇒Windows(?=NT∣XP) Windows
匹配成功, 匹配到的Windows
后面的字符是否等于NT
或XP
e.g. Windows 10 Windows XP
⇒
W
i
n
d
o
w
s
(
?
!
N
T
∣
X
P
)
\stackrel{Windows (?!NT|XP)}{\Rightarrow}
⇒Windows(?!NT∣XP) Windows
匹配成功, 匹配到的Windows
后面的字符是否不等于NT
或XP
反向预搜索 : 格式有(?<=xxxxx)
, (?<!xxxxxx)
; 反向预搜索要求的条件是:所在缝隙的 “左侧”,
两种格式分别要求必须能够匹配和必须不能够匹配指定表达式,而不是去判断右侧。与 “正向预
搜索” 一样的是:它们都是对所在缝隙的一种附加条件,本身都不匹配任何字符。
e.g. 1234567890123456
⇒
(
?
<
=
\
d
{
4
}
)
\
d
+
(
?
=
\
d
{
4
}
)
\stackrel{(?<=\backslash d\{4\})\backslash d+(?= \backslash d\{4\})}{\Rightarrow}
⇒(?<=\d{4})\d+(?=\d{4}) ccccc
匹配成功, 正则表达式的含义是将匹配除了前 4 个数字和后 4 个数字之外的中间 8 个数字
其他通用规则
-
正则表达式中可以使用
\xXX
和\uXXXX
表示一个字符 (X
是以十六进制的形式表示)\xXX
: 编号在 0~ 255 范围的字符- 空格可以使用
\x20
\uXXXX
: 任何字符可以使用\u
再加上其编号的4位十六进制数表示\u4E2D
-
\s
,\d
,\w
,\b
已经表示了特殊的意义,但对应的大写字母表示相反的意义。\S
: 匹配所有非空白字符\D
: 匹配所有的非数字的字符\W
: 匹配所有的字母、数字、下划线以外的字符\B
: 匹配非单词边界,即左右两边都是\w
范围或者两边都不是\w
范围时的字符缝隙
-
在表达式中有特殊意义,需要转义符转义才能得到本身的字符
\^
\$
\(
和\)
\[
和\]
\{
和\}
\.
\?
\+
\*
\|
-
如果
()
内的子表达式如果希望不被记录可以使用(?:xxxxx)
的格式- e.g.
a bbccdd efg
⇒ ( ? : ( \ w ) \ 1 ) \stackrel{(?:(\backslash w)\backslash 1)}{\Rightarrow} ⇒(?:(\w)\1)bbccdd
匹配成功, 括号(?:)
范围的匹配结果不进行记录,因此(\w)
使用\1
来引用
- e.g.
最后
几点建议
- 如果要求表达式所匹配的内容是整个字符串,而不是从字符串中找一部分,那么可以在表达式的首尾使用
^
和$
,比如:^\d+$
要求整个字符串只有数字。 - 如果要求匹配的内容是一个完整的单词,而不会是单词的一部分,那么在表达式首尾使用
\b
,比如:使用\b(if|while|else|void|int……)\b
来匹配程序中的关键字。 - 合理选择贪婪模式与非贪婪模式。
- 表达式不要匹配空字符串。否则会一直得到匹配成功,而结果什么都没有匹配到。
- 或
|
的左右两边,对某个字符最好只有一边可以匹配,这样,不会因为|
两边的表达式因为交换位置而有所不同。 - 能匹配空字符串的子匹配不要循环无限次。
- 多些练习正则表达式的用法, 光看是记不住的和练不熟的。它可以很灵活地使用,但只要掌握基础就很容易能上手使用
举个使用正则表达式的例子:
- 这是哔哩哔哩网站首页的部分源代码(具体的可以到哔哩哔哩网站首页,鼠标右键点击查看源代码即可复制到源代码)
- 将源代码复制到VScode的集成开发环境中
比如:我们要提取出所有的视频链接
-
观察视频链接的规律
-
点击开启VScode的查找功能, 并开启正则表达式的功能, 输入
https://www.bilibili.com/video/.*?(?=")
即可匹配到所有视频链接
-
选所有匹配项后,
Ctrl
+C
复制出来即可
16条视频链接
ok, 上述只是个简单的正则表达式的操作, 还有很多玩法可自行探索!