Jimmy那些事儿

正则表达式

正则表达式。

正则表达式

  • what:记录文本规则的代码

元字符

匹配对象

符号 含义 解释
. 点号 匹配单个(除换行符以外)任意字符 (含空格、下划线等)
\s 空白/空格- space 匹配任意的空白符
\S 非空白字符 匹配任何非空白字符,相当于[^ fv]
\d 数字 - digit 匹配数字 ,相当于[0-9]
\D 非数字 匹配任何非数字字符,相当于[^0-9]
\w 任何字符 - whatever 匹配任何字母数字字符,相当于[a-zA-Z0-9_]
\W 非任何字符 匹配任何非字母数字字符,相当于[^a-zA-Z0-9_]
[ ] 字符组 匹配单个列出的字符,表示或的关系
[^ ] 排除性字符组 匹配单个未列出的字符
\char 转义字符

对于 ? 和 * 的对象在”匹配成功”时也可能并没有匹配任何内容。因为它们允许匹配的数量为0

即便是排除性字符,也需要匹配一个字符

转义符 \\*第一个转义符是对\进行转义,表示:查找 \*此类字符的内容

空格 也是一个字符

字符组 [ ]

  1. 字符组内匹配的是列出的单个字符或者的关系

  2. 字符组元字符 :字符组内的元字符,其含义表示为本身的含义,即代表普通文本字符,相当于转义之后的字符(连字符 - 除外)

    • 连字符 ( - )表示一个范围【注意:只有在字符组内部,连字符( - )才是元字符】
    1
    [0-9] # 表示匹配的字符为0~9之间的任意一个
    • 连字符 ( - ) 出现在字符组的开头,则仅代表普通文本字符
    1
    [-09] # 表示匹配的字符为 - 或者 0 或者 9

量词 - 计数功能

对前一个/一组字符匹配的的次数

符号 含义 解释
= 0 或 1 问号 不匹配,或仅匹配一次
* $\ge$ 0 星号 可能不匹配,也可能匹配任意多次
+ $\ge$ 1 加号 至少匹配一次,也可能匹配任意多次
{m} 对于前一个字符重复m次
{min, max} 区间量词 对前一个字符重复min到max次

匹配位置

符号 含义 解释
^ 脱字符 一行的开头位置
$ 美元符 一行的结束位置
\b 单词分界符 匹配单词的开始或结束 (用于精确匹配)
\D 非单词分界符 匹配不是单词开头或结束的位置

其他元字符

符号 含义 解释
( ) 括号 限定多线结构的范围; 为反向引用“捕获”文本
\1 , \2 反向引用 匹配之前的第一个、第二个括号内的字表达式所匹配的文本(即捕获的文本)
\ 或者 匹配任意分隔的表达式

括号与反向引用的示例

  • 分组0对应整个正则表达式
1
2
3
4
> \b(a-zA-Z)*+\1\b
> # 若 \b(a-zA-Z)*+ 任意字符找到的以单词开头的内容为the,则\1 表示其末尾也应为the的内容
> # 若有两个括号,\2表示第二个括号中所匹配的文本内容
>

多选结构

  1. 使用圆括号( ) 来进行表示,而非方括号

    1
    2
    3
    4
    5
    6
    # 查找 gray 或 grey的写法
    1. gr[ae]y
    2. gr(a|e)y
    # 错误的写法 gr[a|e]y -表示查找 a 或者 | 或者 e

多选结构可以匹配任意长度的字符,而字符组[ ]只能匹配单个字符

示例

你在一篇英文小说里查找hi,你可以使用正则表达式hi

通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。

很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b

\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词

Python - re模块

由于Python的字符串本身也用\转义,因此强烈建议使用Python的r前缀,就不用考虑转义的问题了:

1
2
3
s = r'ABC\-001' # Python的字符串
# 对应的正则表达式字符串不变:
# 'ABC\-001'

分组

()表示的就是要提取的分组(Group),进行提取子串

  1. ^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:
1
2
3
4
5
6
7
8
9
10
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0) # group(0) 永远是原始字符串
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
>>> m.group() # 返回所有的值
  1. 如果正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来。
    • 注意到group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串

贪婪匹配

  1. 正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。
    • 由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0*只能匹配空字符串了。
1
2
>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')
  1. 必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪匹配:
1
2
>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')

编译

当我们在Python中使用正则表达式时,re模块内部会干两件事情:

  1. 编译正则表达式,如果正则表达式的字符串本身不合法,会报错;
  2. 用编译后的正则表达式去匹配字符串。

如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:

1
2
3
4
5
6
7
8
>>> import re
# 编译:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')

使用方式为 命名的变量.方法(查找对象的范围)