Jimmy那些事儿

Python_正则表达式_RE

Re模块

Re模块的函数主要分三个大类:匹配模式、替换、拆分

Python字符串转义符 \

因此强烈建议使用Python的r前缀,就不用考虑转义的问题了:

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


编译

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

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

Python代码最终被被编译为字节码,然后才被解释器执行。在匹配模式之前,正则表达式模式必须先被编译成regex对象。由于正则表达式在执行过程中多次被用于比较,所以强烈建议先对它进行预编译。

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


compile - 模块的函数

  • re.compile(patter [, flags=0]) 把正则表达式编译成一个正则对象(regex);供其他函数 find()、match()、search() 等函数使用
    • 使用方式为 命名的变量.方法(查找对象的范围) ;其实因为在其他Re函数使用时,自动传递pattern
    • 若对许多字符串应用同一条正则表达式,强烈建议通过re.compile创建对象。

flags 包括:
re.I 忽略大小写
re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
re.M 多行模式
re.S 即为’ . ’并且包括换行符在内的任意字符(’ . ’不包括换行符)
re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库
re.X 为了增加可读性,忽略空格和’ # ’后面的注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
a = re.compile() # 创建的正则规则
result = a.findall(ojbect)
# ----【 简单说明 】
compiled_pattern = re.compile(pattren)
resule = re.findall(compiled_pattern, string) # 这种方式更利于理解
#---------等价于--------------
compiled_pattern = re.compile(pattren)
result = compiled_patter.findall(string) # 使用方式为 命名的变量.方法(查找对象的范围) 。其实因为在其他Re函数使用时,自动传递pattern
# ----【 解释说明 】
## p 即为编译后的 pattern。 在其他Re函数使用时,自动传递pattern
p = re.compile( '(one|two|three)')
p.sub( 'num', 'one word two words three words') # 其第一个参数为p,即将one|two|three作为查找的对象,替换为rep = num
'num word num words num words'
### 正常应为 re.sub('(one|two|three)', 'num', 'one word two words three words')
>>> 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')



Re模块的函数和regex对象的方法

强调:查找的对象是 字符串string

search & match- 搜索&匹配

search - 搜索; match - 匹配

通过.group 返回对象

  • match(pattern, string, flags=0):在字符串string的开头 开始搜索正则表达式模式pattern;若匹配成功,则返回一个匹配对象(仅返回匹配的部分);否则返回None
  • search(pattern, string, flags=0):从字符串string中(任意位置)搜索正则表达式模式pattern 第一次出现的地方; 如果匹配成功,则返回一个匹配对象;否则返回None
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# match 应用
re.match('foo', 'food on the table')
<_sre.SRE_Match object; span=(0, 3), match='foo'>
re.match('foo', 'The food on the table') # 什么都没有返回
# search 应用
re.search('foo', 'The food on the table')
<_sre.SRE_Match object; span=(4, 7), match='foo'>
val = 'abc, dca, guides'
m = re.search('uid', val)
# <_sre.SRE_Match object; span=(9, 12), match='uid'>
val[m.start():m.end()]
# 'uid'


findall & finditer - 查找

search 与 findall 不同之处在于:findall 总返回一个[ 列表 ]

  • findall(pattern, string [,flags]) :在字符串string中搜索匹配pattern的所有内容,并返回一个匹配对象的 [列表 list]

正则表达式

  • finditer(pattern, string [, flags]):在字符串string中搜索匹配pattern的所有内容,返回一个迭代器,该迭代器返回一个匹配对象


sub - 替换

  • re.sub(pattern, repl ,string, max = 0) : 把字符串string中所有匹配正则表达式pattern的地方 替换成 字符串 repl
    • 如果没有找到匹配 pattern 的串,则返回未被修改的 string。Repl 既可以是字符串也可以是一个函数。

若max 的值没有给出,则对所有匹配的地方进行替换

  • re.subn - 该函数的功能和 sub() 相同,但它还返回新的字符串以及替换的次数
1
2
3
4
5
6
7
8
9
10
re.sub(pattern, repl, string[, count, flags])
# count=1 表示替换仅替换第一个
p = re.compile( '(one|two|three)')
p.sub( 'num', 'one word two words three words') # 其第一个参数为p,即将one|two|three作为查找的对象,替换为rep = num
'num word num words num words'
# ------------------------------------
subn(repl, string[, count=0])


split - 分割

  • re.split(pattren, string, max=0) : 根据正则表达式pattern中的分隔符 把字符串string 分割为一个 [列表]

若max 的值没有给出,则对所有匹配的地方进行分割

1
re.split(pattern, string[, maxsplit=0, flags=0])


group - 匹配对象

匹配对象的方法:在match & search 被成功调用之后所返回的结果。

  • group(num=0)返回全部匹配对象(或指定编号是num的子组)
  • groups():返回一个包含唯一或全部匹配的子组的元组(若正则表达式中没有子组,则返回一个空元组)

num 代表第1、2、……个子串。如果正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# group 应用
re.match('foo', 'food on the table').group()
'foo'
# groups 应用
re.match('foo', 'food on the table').groups()
()
# 用()定义组
^(\d{3})-(\d{3,8})$ # 分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:
>>> 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. 正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。
    • 由于\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')


参考资料 : 正则表达式

元字符

匹配对象

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

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

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

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

空格 也是一个字符

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

字符组 [ ]

  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这个单词


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import os
import re
f = open('caihf001')
text = f.read()
f.close()
k = re.compile('Lemon', re.I)
k.findall(text)
# ----效果同上
with open('caihf001') as f:
text = f.read()
# print(text)
k = re.compile('lemon',re.I)
l = re.findall(k,text)
print(l)
['Lemon', 'lemon', 'Lemon', 'Lemon', 'LeMoN', 'LEmon', 'LEMON']