[Skill]从零掌握正则表达式
前言
无论你是出于什么原因需要掌握正则表达式(诸如爬虫、文本检索、后端服务开发或Linux
脚本),如果之前从没接触过正则表达式(比如我)很容易在如山般的公式中迷失,以至于你在项目写的正则表达式很可能会因为组织混乱而被后来的开发者吐槽。
正则表达式Regular Expression
本质上是一种文本模式,包括普通字符和特殊字符(也被称为元字符),使用一个字符串表达式来匹配符合该规则的字符串。
学习工具:正则表达式可视化工具
这个网站可以可以用可视化的方式展示正则表达式的匹配模式,比如^[0-9]+abc$
表示从字符串的开头开始匹配一或多个数字,然后以abc
结尾。
这个网站可以用于测试你写的正则表达式匹配效果,比如Re(\w)+
表示匹配以Re
开头的字符串。
从通配符讲到正则表达式
在操作系统上或者SQL
中我们经常接触到通配符的使用,比如模糊搜索文件(比如*.dat
表示匹配所有以.dat
为后缀的文件)。常用的通配符包括:
-
?
:匹配0
或者1
个任意字符 -
*
:匹配0
或者多个任意字符
上面这两个通配符尽管用途很广,但很难穷尽任意字符串匹配的场景,正则表达式的功能更加强大,足以覆盖字符串匹配的大部分场景。上面网站展示的两个例子已经说明其的强大之处,接下来我们只需要掌握它的语法即可。
正则表达式最常用的几个用途包括:
- 字符串模式校验:比如我们后端的
http
服务受到参数时校验该字符串是否是日期、电话和身份证等 - 文本批量替换:可以对满足匹配的规则的文本进行全部替换
- 从字符串中提取子字符串:比如在爬虫的时候从整个
html
页面中提取需要的子字符串 - 检查一个字符串中是否包含某个类型的字符串
正则表达式语法
普通字符包括没有被显式指定为元字符的所有可打印和非打印字符,包括所有的大写和小写字母、数字、标点符号和一些其他符号。
1. 非打印字符
非打印字符也可以是正则表达式的组成部分。
-
\cx
:匹配由x
指明的控制字符,例如\cM
匹配一个Control-M
或者回车符,x
的值必须为A-Z
或a-z
之一 -
\f
:匹配一个换页符 -
\n
:匹配一个换行符 -
\r
:匹配一个回车符 -
\s
:匹配任何空白字符,包括空格、制表符和换页符等,等价于[\f\n\r\t\v]
-
\S
:匹配任何非空白字符,等价于[^\f\n\r\t\v]
-
\t
:匹配一个制表符 -
\v
:匹配一个垂直制表符
2. 特殊字符
如果要匹配这些特殊字符的话,可以在其前面加上一个反斜杠进行转义。
-
\$
:匹配输入字符串的结尾位置,如果设置了RegExp
的Multiline
属性则$
也匹配\n
或\r
-
()
:标记一个子表达式的开始和结束位置,子表达式可以获取供以后使用 -
*
:匹配前面的子表达式零次或多次 -
+
:匹配前面的子表达式一次或多次 -
.
:匹配除换行符\n
外的任意单个字符 -
[
:标记一个中括号表达式的开始 -
?
:匹配前面的子表达式零次或一次,或指明一个非贪婪限定符 -
\
:将下一个字符标记为或特殊字符、或原义字、或向后引用、或八进制转义符 -
^
:匹配输入字符串的开始位置,如果实在方括号表达式中使用表示不接受该方括号表达式中的字符集合 -
{
:标记限定符表达式的开始 -
|
:指明两项之间的一个选择
3. 限定符
限定符用于指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。
-
*
:匹配前面的子表达式零次或多次 -
+
:匹配前面的子表达式一次或多次 -
?
:匹配前面的子表达式零次或一次 -
{n}
:匹配确定的n
次 -
{n,}
:至少匹配n
次 -
{n,m}
:最少匹配n
次最多匹配m
次
注意
*
和+
限定符都是贪婪的,因为它们会尽可能多的匹配文案,只要在它们的后面加上一个?
就可以实现非贪婪或者最小匹配。
4. 定位符
-
^
:匹配输入字符串开始的位置 -
$
:匹配输入字符串结尾的位置 -
\b
:匹配一个单词边界,即字与空格间的位置 -
\B
:非单词办结匹配
5. 选择
用圆括号将所有选择项括起来,相邻的选择项之间用|
发那个,但用圆括号会有一个副作用使得相关的匹配都被缓存,此时可用?:
放在第一个选项前来消除这种副作用。
6. 反向引用
对一个正则表达式模式或者部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从1
开始,最多可以存储99
个捕获的字表达式,每个缓冲区都可以用\n
访问。
可以使用非捕获元字符
?:
、?=
或?!
来重写捕获。
反向引用有两个主要的用途:
- 用于寻找文本中两个相同的相邻单词匹配项
相当于查询重复出现两次的单词,下面的python
代码举了一个例子:
import re
"""
re.search(pattern, string, flags=0)
pattern: 匹配的正则表达式
string: 要匹配的字符串
flags: 标志位, 用于控制正则表达式的匹配方式, 包括是否区分大小写和多行匹配等
"""
# 查询重复出现两次的单词
target_str = "Is is the cost of of gasoline going up up"
match_str = re.findall(r'\b([a-z]+) \1\b', target_str, re.M|re.I)
print("match str : ", match_str)
==================================================================
# 输出结果
# match str : ['of', 'up']
- 还可用于将字符串分解成多个组件
比如我们可以将url
解析成域名、端口等元素:
import re
"""
re.search(pattern, string, flags=0)
pattern: 匹配的正则表达式
string: 要匹配的字符串
flags: 标志位, 用于控制正则表达式的匹配方式, 包括是否区分大小写和多行匹配等
返回值: 是一个list, 表示所有匹配到的子字符串
"""
target_str = "http://www.runoob.com:80/html/html-tutorial.html"
match_str = re.findall(r'(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)', target_str, re.M|re.I)
print("match str : ", match_str[0])
==================================================================
# 第一个括号子表达式包含 http
# 第二个括号子表达式包含 www.runoob.com
# 第三个括号子表达式包含 :80
# 第四个括号子表达式包含 /html/html-tutorial.html
# 输出结果
# match str : ('http', 'www.runoob.com', ':80', '/html/html-tutorial.html')
7. 元字符
-
\
:将下一个字符标记为特殊字符,或原义字符,或向后引用,或一个八进制转义符 -
^
:匹配输入字符串的开始位置 -
$
:匹配输入字符串的结束为止 -
*
:匹配前面的子表达式零次或多次 -
+
:匹配前面的子表达式一次或多次 -
?
:匹配前面的子表达式零次或一次 -
{n}
:匹配确定的n
次 -
{n,}
:匹配至少n
次 -
{n,m}
:匹配至少n
次,至多m
次 -
?
:当该字符紧跟在任何一个其他限制符后面时表示匹配模式是非贪婪的 -
.
:匹配除换行符\n \r
外的任何单个字符 -
(pattern)
:匹配pattern
并捕获这一结果 -
(?:pattern)
:匹配pattern
并不捕获这一结果 -
(?=pattern)
:正向肯定预查,在任何匹配pattern
的字符串开始处匹配查找字符串,这是一个非捕获匹配,即该匹配不需要获取供以后使用 -
(?!pattern)
:正向否定预查,在任何不匹配该pattern
的字符串开始处匹配字符串,这是一个非获取匹配,即该匹配不需要获取供以后使用 -
(?<=pattern)
:反向肯定预查,与正向肯定预查类似,只是方向相反 -
(?<!pattern)
:反向否定预查,与正向否定预查类似,只是方向相反 -
x|y
:匹配x
或y
-
[xyz]
:匹配所包含的任意一个字符 -
[^xyz]
:匹配未包含的任意字符 -
[a-z]
:匹配指定范围内的任意字符 -
[^a-z]
:匹配任何不在指定范围内的任意字符 -
\b
:匹配一个单词边界,即单词和空格间的位置 -
\B
:匹配非单词边界 -
\cx
:匹配由x
指明的控制字符 -
\d
:匹配一个数字字符 -
\D
:匹配一个非数字字符 -
\f
:匹配一个换页符 -
\n
:匹配一个换行符 -
\r
:匹配一个回车符 -
\s
:匹配任何空白字符,等价于[\f\n\r\t\v]
-
\S
:匹配任何非空白字符 -
\t
:匹配一个制表符 -
\v
:匹配一个垂直制表符 -
\w
:匹配字符、数字、下划线,等价于[A-Za-z0-9]
-
\W
:匹配非字符、数字、下划线 -
\xn
:匹配n
,其中n
为十六进制转义值 -
\n
:标记一个八进制转义值或向后引用
运算符优先级
运算符优先级从高到低如下:
-
\
:转义符 -
(),(?:),(?=),[]
:圆括号和方括号 -
*, +, ?, {n}, {n,}, {n,m}
:限定符 -
^, $, \任何元字符、任何字符
:定位点和序列(即位置和顺序) -
|
:“或”操作