正则表达式(Regular Expression)
- 类别:工具语言,内嵌于其他语言或者产品内的迷你语言,几乎所有的语言或者工具都支持正则表达式
- 形式:字符串(字符集合)
- 用途:模式匹配搜索,替换。
- 起源:20实际50年代数学研究领域
- 早期发展:UNIX中的Perl语言和grep工具
一、 简单使用正则表达式:
1、匹配静态纯文本
"NAME"
"name"
这是对正则表达式的一种浪费
2、匹配任意单个字符
"."
正则表达式中的 ‘.’ 字符可以匹配任意一个字符,相当于DOS中”?“符号,相当于SQL模糊匹配中的”_"符号。
匹配任意连续的两个字符
".."
3、匹配特殊字符(转义)
-
元字符:这个字符有特殊含义,而不是字符本身含义
-
实例:反斜杠”\“
转义.符号
"\."
转义\符号
"\\"
4、匹配一组字符
使用两个元字符[和]来表示一组集合
匹配R和r
"[Rr]"
匹配字符区间
// 匹配字符
"[a-z]"
"[A-Z]"
// 匹配数字
"[0-9]"
"[0-9]“的作用和”[0123456789]"的作用相同
注意:
- 表示匹配区间的时候首字符要大于尾字符
- "-"字符出现在[ ]中的时候表示的是一个元字符
在一个字符区间中可以给定多个字符区间
"[a-z0-9A-Z]"
"[abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ]"
5、取非匹配(^)
取非数字集合
"[^0-9]"
取非Rr
"[^Rr]"
二、使用元字符
元字符:有特殊意义的字符,这些字符不能用来代表他们本身。
例子:不能用[来匹配[本身。
"Array\[5\]"
将会匹配得到:
"Array[5]"
1、匹配空白元字符
元字符 | 说明 |
---|---|
[\b] | 回退(并删除)一个字符(BackSpace键) |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\t | 制表符(Tab键) |
\v | 垂直制表符 |
2、匹配数字和非数字
元字符 | 说明 |
---|---|
\d | 匹配所有数字,相当于[0-9] |
\D | 匹配所有非数字,相当于[^0-9] |
3、匹配字母和数字
元字符 | 说明 |
---|---|
\w | 匹配所有字母,数字和下划线,相当于[a-zA-Z0-9_] |
\W | 匹配所有非数字,相当于[^a-zA-Z0-9_] |
4、匹配空白字符和非空白字符
元字符 | 说明 |
---|---|
\s | 匹配所有空白字符,相当于[\f\n\t\r\v] |
\S | 匹配所有非数字,相当于[^\f\n\t\r\v] |
SQL中的转义需要使用两个\
mysql> select ename , hiredate from emp where hiredate regexp '[0-9][0-9][0-9][0-9]-\\d\\d-[0-9][0-9]'; +--------+------------+ | ename | hiredate | +--------+------------+ 吗1 | SMITH | 1980-12-17 | | ALLEN | 1981-02-20 | | WARD | 1981-02-22 | | JONES | 1981-04-02 | | MARTIN | 1981-09-28 | | BLAKE | 1981-05-01 | | CLARK | 1981-06-09 | | SCOTT | 1987-04-19 | | KING | 1981-11-17 | | TURNER | 1981-09-08 | | ADAMS | 1987-05-23 | | JAMES | 1981-12-03 | | FORD | 1981-12-03 | | MILLER | 1982-01-23 | +--------+------------+ 14 rows in set (0.00 sec)
5、匹配十六进制和八进制
元字符 | 含义 |
---|---|
\x | 匹配十六进制,例如:\x0A对应ASCII字符10(换行符) |
\0 | 匹配八进制,例如\011对应ASCII字符9(制表符),等价于\t |
6、POSIX字符类运用
字符类 | 说明 |
---|---|
[:alnum:] | 任何一个字母或者数字 |
[:alpha:] | 任何一个字母 |
[:blank:] | 空格或者制表符 |
[:cntrl:] | ASCII控制符 |
[:digit:] | 任何一个数字 |
[:graph:] | 和[:print:]一样,但不包含空格 |
[:lower:] | 任何一个小写字母 |
[:upper:] | 任何一个大写字母 |
[:print:] | 任何一个可打印字符 |
[:punct:] | 既不属于[:alnum:]也不属于[:cntrl:]的任何一个字符 |
[:space:] | 任何一个空白字符,包括空格 |
[:xdigit:] | 任何一个十六进制数字 |
三、重复匹配
1、匹配一个或者多个(+)
REGEXP | 含义 |
---|---|
a+ | 匹配一个或者多个a,至少有一个a |
\w+ | 匹配一个或多个字母,数字,下划线 |
[0-9]+ | 匹配一个或者多个数字 |
2、匹配零个或者多个字符(*)
REGEXP | 含义 |
---|---|
a* | 匹配零个或者多个a |
\w* | 匹配零个或者多个字母,数字,下划线 |
[0-9]* | 匹配零个或者多个数字 |
3、匹配零个或者一个字符(?)
RETEXP | 说明 |
---|---|
a? | 匹配一个或者两个a |
\w? | 匹配任意多个字母,数字,下划线 |
[0-9]? | 匹配一个或者两个数字 |
4、指定重复匹配数量({n},{m,n},{m,})
REGEXP | 说明 |
---|---|
a{n} | 必须连续出现n个a才算一个匹配 |
\w{n} | 必须连续出现n个字母,数字,下划线才算一个匹配 |
匹配RGB的值
"#[[:xdigit:]]{6}"
#546987
REGEXP | 说明 |
---|---|
a{m,n} | 必须连续出现至少m,最多n个a才算一个匹配 |
\w{m,n} | 必须连续出现至少m,最多n个字母,数字,下划线才算一个匹配 |
REGEXP | 说明 |
---|---|
a{m,} | 至少有连续的m个a才算一个匹配 |
关于过度匹配:{n}和{m,n}所定义的重复次数也是有限制的,遇到某些特殊的文本时候就会出现过度匹配的问题
例子:
原文:
This offer is not available to customers living in <B>AK</B> and <B>HI</B>
正则表达式:
"<[Bb]>.*</[Bb]>"
结果:
<B>AK</B> and <B>HI</B>
预期结果:
<B>AK</B>
<B>HI</B>
分析:
*和+都是“贪婪型”元字符,他们在匹配的时候是尽可能多的去匹配,而不是适可而止。
在不需要这种贪婪型的匹配的时候,我们应该使用“懒惰型” 的元字符去匹配
懒惰型元字符:
懒惰型的元字符写法简单,就是在贪婪型的元字符后加上一个限定符“?”
贪婪型元字符 | 懒惰型元字符 |
---|---|
* | *? |
+ | +? |
{n,} | {n,}? |
为了解决上述例子中我们遇到第一个匹配的时候就应结束继续寻找第二个匹配的问题,我们使用“懒惰型”元字符
"<Bb>.*?</Bb>"
匹配结果:
<B>AK</B>
<B>HI</B>
四、位置匹配
位置匹配用来解决再什么地方进行字符串匹配的问题。
边界限定符:
1、单词边界
- 限定符:\b
- 意义:表示一个单词的开始和结束
如果想要匹配一个完整的单词,就要在这个文本的前后都加上这个限定符,否则就会溢出边界进行匹配。
2.非单词边界:
- 限定符:\B
- 意义:与\b的用法刚哈后相反
五、子表达式
1、子表达式解决的问题
上面提到的所有用于表示匹配次数的元字符都是作用于它前面的那个字符,而当我们期望它作用于一个串的时候,往往就达不到期望效果,所以这里提出了子表达式的概念。
元字符 | 说明 |
---|---|
() | 表示一个独立的元素 |
用正则表达式去匹配IP地址:
- 方案一:
"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
- 方案二:
"(\d{1,3}\.){3}\d{1,3}"
再次强调作用域问题:
例如:用正则表达式找出19和20的年份:
方案一:
"19|20\d{2}"
结果:
这样只会匹配19和2000,2021,…等以20开头的4位数字
方案二:
"(19|20)\d{2}"
2、子表达式嵌套
子表达式实可以递归嵌套的,嵌套的次数是没有限制的。
解决问题:
再回到匹配IP地址的问题上来。
上面我们用到的模式:
"(\d{1,3}\.)\d{1,3}"
这个模式虽然是匹配到了用三个句点分割的四组数字,但是这个结果并不一定是一个合法的IP地址,那么这样一来确实能匹配到一些非法的IP地址。
IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”(也就是4个字节)。IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制整数。例:点分十进IP地址(100.4.5.6),实际上是32位二进制数(01100100.00000100.00000101.00000110)。
4组数字的匹配规则:
- 任何一个1位或者2位的数字
- 任何一个1开头的3位数字
- 任何一个2开头,第二位数字在0~4之间的3位数字
- 任何一个25开头,第三位数字在0~5之间的3位数字
正则表达式:
"(((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5]))"
六、回溯引用匹配
例子:匹配一段文本中连续出现的单词时候,用回溯匹配解决
"[ ]+(\w+)[]\1"
注意:
-
"(\w+)[]+\1"
是一个整体,(\w+)
这个子表达式不是用来重复匹配的,他是和后面的\1来联合使用的。 -
\n
表示代表第n个子表达式(在整个模式表达式中的相对位置) -
\0
表示匹配整个模式 -
上述例子中,
\w+
匹配到啥,\1
也匹配啥。 -
回溯引用只能引用模式里的子表达式(用括号括起来的表达式)
-
回溯引用可以想象成变量
问题:匹配HTML中的多级标题
"<[Hh]([1-6])>.*?</[Hh]\1>"
七、替换操作
利用回溯引用可以达到替换操作。
- 搜索模式:用来找到替换目标的表达式,可以跨模式使用
"(\w+[\w\.]*@{\w+\.]+\.\w+)"
- 替换模式:用于替换的模式,需要使用引用。
<A HREF = "mailto:$1"> $1 </A>
大小写转换
元字符 | 说明 |
---|---|
\U | 把\U和\E之间的所有字符转换为大写 |
\L | 把\L和\E之间的所有字符转换为小写 |
\E | 结束\U 或者\L 转换 |
\u | 把下一个字符大写 |
\l | 把下一个字符小写 |
例子:
(<Hh 1>)(.*?)(</[Hh] 1>)
$1\U$2\E$3
八、前后查找
包含的匹配本身并不返回,它只是用来确定匹配的位置,并不是匹配结果的一部分。
1、向前查找
-
格式:
?=
后面的内容是匹配位置标记 -
表示匹配到后面这个内容就可以结束了,但是不返回后面的这个内容作为匹配结果
".+(?=:)"
这是一个用来匹配协议名称的模式
它能匹配
https
,http
,ftp
等内容,只匹配:号之前的内容
2、向后查找
- 格式:
?<=
后面的内容是匹配位置标记 - 表示以后面内容开始查找,但是只返回以这个内容开始的后面内容
"(?<=\$)[0-9.]+
结果:
匹配价格,但是不包括$符号
3、向前查找和向后查找结合起来使用
"(?<=[tT][iI][tT][lL][eE]).*?(?=/[tT][iI][tT][lL][eE])"
结果:
匹配HTML的TITLE标签
4、前后查找取非
操作符 | 说明 |
---|---|
(?=) | 查找指定内容之前的内容作为匹配结果 |
(?!) | 查找不是以指定内容结尾的内容作为匹配结果 |
(?<=) | 查找指定以内容开始的内容作为匹配结果 |
(?<!) | 擦护照不是以指定内容开始的内容作为匹配结果 |
例子:
查找不是价格的数字
"\b(?<!\$)\d+\b"