15.正则表达式

一、 常用语法

. * + ? \ [ ] ^ $ { } | ( )

1. 点 . —- 匹配所有字符

. :表示要匹配除了 换行符 之外的 所有 单个 字符 。

1
2
3
4
5
6
7
8
9
content = '''苹果是绿色的
橙子是橙色的
香蕉是黄色的
乌鸦是黑色的'''

import re
p = re.compile(r'.色')
for one in p.findall(content):
print(one)

执行结果:.色

1
2
3
4
绿色
橙色
黄色
黑色

2. 星号 * —- 重复匹配任意次

* :表示匹配前面的子表达式任意次,包括 0 次

1
2
3
4
5
6
7
8
9
10
content = '''苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,'''

import re
p = re.compile(r',.*')
for one in p.findall(content):
print(one)

执行结果:,.*

1
2
3
4
5
,是绿色的
,是橙色的
,是黄色的
,是黑色的

  • 紧跟在 . 后面, 表示 任意字符可以出现任意次, 所以整个表达式的意思就是在逗号后面的 所有字符,包括逗号

  • .* 在正则表达式中非常常见,表示匹配任意字符任意次数。

  • * 前面不是非得是 ,也可以是其它字符

3. 加号 + —- 重复匹配多次

+ :表示匹配前面的子表达式一次或多次,不包括 0 次

1
2
3
4
5
6
7
8
9
10
content = '''苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,'''

import re
p = re.compile(r',.+')
for one in p.findall(content):
print(one)

执行结果:,.+

1
2
3
4
,是绿色的
,是橙色的
,是黄色的
,是黑色的

4. 问号 —- 匹配 0 ~ 1 次

? :表示匹配前面的子表达式 0 次或 1 次。

1
2
3
4
5
6
7
8
9
10
content = '''苹果,是绿色的
橙子,橙色的
香蕉,黄色的
乌鸦,黑色的
猴子,'''

import re
p = re.compile(r',.?')
for one in p.findall(content):
print(one)

执行结果:,.?

1
2
3
4
5
,绿
,橙
,黄
,黑

5. 花括号 {} —- 匹配指定次数

{} :表示 前面 的字符匹配 指定的次数

1
红彤彤,绿油油,黑乎乎,绿油油油油

执行结果:油{3,4}

  • 油{3} :就表示匹配 连续的 油 字 3 次
  • 油{3,4} :就表示匹配 连续的 油 字 至少 3 次,至多 4 次

6. 贪婪模式 和 非贪婪模式

*+ :在正则表达式中都是 贪婪的 ,他们会尽可能多的去匹配。

非贪婪模式,也就是在星号后面加上 ?

1
2
3
4
5
6
7
source = '<html><head><title>Title</title>'

import re
# 注意多出的问号
p = re.compile(r'<.*?>')

print(p.findall(source))

执行结果:

1
['<html>', '<head>', '<title>', '</title>']

7. 对元字符转义

\ :反斜杠配合元字符使用,可以对元字符进行 转义 ,使其成为普通字符

1
2
3
4
5
6
7
8
content = '''苹果.是绿色的
橙子.是橙色的
香蕉.是黄色的'''

import re
p = re.compile(r'.*\.')
for one in p.findall(content):
print(one)

执行结果:.*\.

1
2
3
苹果.
橙子.
香蕉.

8. 匹配某种字符类型

\d :匹配 0-9 之间任意一个数字字符,等价于表达式 [0-9]

\D :匹配任意一个不是 0-9 之间的数字字符,等价于表达式 [^0-9]

\s :匹配任意一个空白字符,包括 空格、tab、换行符等,等价于表达式 [\t\n\r\f\v]

\S :匹配任意一个非空白字符,等价于表达式 [^ \t\n\r\f\v]

\w :匹配任意一个文字字符,包括大小写字母、数字、下划线,等价于表达式 [a-zA-Z0-9_]缺省情况也包括 Unicode文字字符,如果指定 ASCII 码标记,则只包括 ASCII 字母

\W :匹配任意一个非文字字符,等价于表达式 [^a-zA-Z0-9_]

反斜杠 也可以用在方括号里面,比如 [\s,.] 表示匹配 : 任何空白字符, 或者逗号,或者点

9. 方括号 [] —- 匹配几个字符之一

[] :表示要匹配 指定的几个字符之一

比如:

[abc] 可以匹配 a, b, 或者 c 里面的任意一个字符。等价于 [a-c]

[a-c] 中间的 - 表示一个范围从 a 到 c。

::: tip

1.元字符[] 中 为 普通字符

2.在 [] 中使用 ^ 表示 方括号里面的字符合集。

:::

1
2
3
4
5
6
content = 'a1b2c3d4e5'

import re
p = re.compile(r'[^\d]' )
for one in p.findall(content):
print(one)

执行结果:

1
2
3
4
5
a
b
c
d
e

10. 起始、结尾位置 和 单行、多行模式

  • ^ :表示 匹配文本的 开始 位置

  • 默认为 单行模式 , 多行模式 需要增加参数 re.M

    • 单行模式 : 表示匹配 整个文本 的开头位置
    • 多行模式 : 表示匹配 文本每行 的开头位置
    1
    2
    3
    4
    5
    6
    7
    8
    content = '''001-苹果价格-60
    002-橙子价格-70
    003-香蕉价格-80'''

    import re
    p = re.compile(r'^\d+', re.M)
    for one in p.findall(content):
    print(one)

    执行结果:

    1
    2
    3
    001
    002
    003

    去掉 compile 的第二个参数 re.M, 运行结果:

    1
    001
  • $ :表示 匹配文本的 结束 位置

  • 默认为 单行模式 , 多行模式 需要增加参数 re.MULTILINE

    • 单行模式 : 表示匹配 整个文本 的开头位置
    • 多行模式 : 表示匹配 文本每行 的开头位置
    1
    2
    3
    4
    5
    6
    7
    8
    content = '''001-苹果价格-60
    002-橙子价格-70
    003-香蕉价格-80'''

    import re
    p = re.compile(r'\d+$', re.MULTILINE)
    for one in p.findall(content):
    print(one)

    执行结果:

    1
    2
    3
    60
    70
    80

    去掉 compile 的第二个参数 re.MULTILINE, 运行结果:

    1
    80

11. 竖线 | —- 匹配其中之一

| :表示 匹配 其中之一

::: tip

竖线 在正则表达式的优先级 最低 ,意味着, 竖线隔开的部分是 一个整体。

:::

12. 括号 () —- 分组

() : 表示为 正则表达式的 组选择

:就是把 正则表达式 匹配的内容 其中的某些部分 标记为某个组

1
2
3
4
5
6
7
8
content = '''张三,手机号码15945678901
李四,手机号码13945677701
王二,手机号码13845666901'''

import re
p = re.compile(r'^(.+),.+(\d{11})', re.MULTILINE)
for one in p.findall(content):
print(one)

执行结果:

1
2
3
('张三', '15945678901')
('李四', '13945677701')
('王二', '13845666901')

(?P<分组名>...) :使用这样的格式可以给每个分组命名,方便以后调用。

1
2
3
4
5
6
7
8
content = '''张三,手机号码15945678901
李四,手机号码13945677701
王二,手机号码13845666901'''

import re
p = re.compile(r'^(?P<name>.+),.+(?P<phone>\d{11})', re.MULTILINE)
for match in p.finditer(content):
print(match.group('name'), match.group('phone'))

执行结果:

1
2
3
张三 15945678901
李四 13945677701
王二 13845666901

13. 让 点 匹配换行

re.DOTALL 参数 :可以使 点也能匹配 换行符

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
content = '''
<div class="el">
<p class="t1">
<span>
<a>Python开发工程师</a>
</span>
</p>
<span class="t2">南京</span>
<span class="t3">1.5-2万/月</span>
</div>
<div class="el">
<p class="t1">
<span>
<a>java开发工程师</a>
</span>
</p>
<span class="t2">苏州</span>
<span class="t3">1.5-2/月</span>
</div>
'''

import re
p = re.compile(r'class=\"t1\">.*?<a>(.*?)</a>', re.DOTALL)
for one in p.findall(content):
print(one)

执行结果:

1
2
Python开发工程师
java开发工程师

二、 切割字符串

split 方法 :可以切割 不同 分隔符 的字符串

1
2
3
4
5
6
import re

names = '关羽; 张飞, 赵云, 马超, 黄忠 李逵'

namelist = re.split(r'[;,\s]\s*', names)
print(namelist)

执行结果:

1
['关羽', '张飞', '赵云', '马超', '黄忠', '李逵']

[;,\s]\s* 指定了,分割符为 分号、逗号、空格 里面的任意一种均可,并且 该符号周围可以有不定数量的空格。

三、 字符串替换

1. 匹配模式替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import re

names = '''

下面是这学期要学习的课程:

<a href='https://www.bilibili.com/video/av66771949/?p=1' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是牛顿第2运动定律

<a href='https://www.bilibili.com/video/av46349552/?p=125' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是毕达哥拉斯公式

<a href='https://www.bilibili.com/video/av90571967/?p=33' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是切割磁力线
'''

newStr = re.sub(r'/av\d+?/', '/cn345677/' , names)
print(newStr)
  • 第一个参数 /av\d+?/ 这个正则表达式,表示以 /av 开头,后面是一串数字,再以 / 结尾的 这种特征的字符串 ,是需要被替换的。
  • 第二个参数,这里 是 '/cn345677/' 这个字符串,表示用什么来替换。
  • 第三个参数是 源字符串。

2.指定替换函数

sub 函数 : 可以使用 函数 替换指定位置的字符串

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
31
32
33
34
import re

names = '''

下面是这学期要学习的课程:

<a href='https://www.bilibili.com/video/av66771949/?p=1' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是牛顿第2运动定律

<a href='https://www.bilibili.com/video/av46349552/?p=125' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是毕达哥拉斯公式

<a href='https://www.bilibili.com/video/av90571967/?p=33' target='_blank'>点击这里,边看视频讲解,边学习以下内容</a>
这节讲的是切割磁力线
'''


# 替换函数,参数是 Match对象
def subFunc(match):
# Match对象 的 match[0] 返回的是整个匹配上的字符串,
src = match[0]

# Match对象 的 match[1] 返回的是第一个group分组的内容
number = int(match[1]) + 6
dest = f'/av{number}/'

print(f'{src} 替换为 {dest}')

# 返回值就是最终替换的字符串
return dest


newStr = re.sub(r'/av(\d+?)/', subFunc, names)
print(newStr)