地址:https://www.byhy.net/tut/py/extra/regex/
白月黑羽

在线验证正则表达式:https://regex101.com/
国内正则表达式验证网站


常见语法

写在正则表达式里面的普通字符都是表示: 直接匹配它们。
正则前面加个r‘’,
但是有些特殊的字符,术语叫 metacharacters(元字符)。
它们出现在正则表达式字符串中,不是表示直接匹配他们, 而是表达一些特别的含义。
这些特殊的元字符包括下面这些:

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

点-匹配所有字符

. 表示要匹配除了 换行符 之外的任何 单个 字符。 image.png

  1. content = '''苹果是绿色的
  2. 橙子是橙色的
  3. 香蕉是黄色的
  4. 乌鸦是黑色的'''
  5. import re
  6. p = re.compile(r'.色')
  7. for one in p.findall(content):
  8. print(one)

星号-重复匹配任意次

  • 表示匹配前面的子表达式任意次,包括0次。
    image.png
    特别是最后一行,猴子逗号后面没有其它字符了,但是*表示可以匹配0次, 所以表达式也是成立的。
    ```python content = ‘’’苹果,是绿色的 橙子,是橙色的 香蕉,是黄色的 乌鸦,是黑色的 猴子,’’’

import re p = re.compile(r’,.*’)

p是一个Pattern类,好处是对它进行各种方法的处理

for one in p.findall(content): print(type(one)) print(one)

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/2372430/1649491484028-05a55622-bd28-4984-85bd-1ceadc54608e.png#clientId=ud37f0a74-9d70-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u290fb426&margin=%5Bobject%20Object%5D&name=image.png&originHeight=203&originWidth=386&originalType=url&ratio=1&rotation=0&showTitle=false&size=11528&status=done&style=none&taskId=ud03778ec-4085-4b40-a001-8100ffd1608&title=)
  2. <a name="JuFlL"></a>
  3. ## 加号-重复匹配多次(至少一次)
  4. + 表示匹配前面的子表达式一次或多次,不包括0次。 <br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2372430/1649491540706-165da98e-1eb1-427f-ae90-3f4ea169f64f.png#clientId=ud37f0a74-9d70-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u794f9a14&margin=%5Bobject%20Object%5D&name=image.png&originHeight=232&originWidth=385&originalType=url&ratio=1&rotation=0&showTitle=false&size=12450&status=done&style=none&taskId=ue98c0acf-2478-4cb9-a15e-1e69549266e&title=)<br /> 最后一行,猴子逗号后面没有其它字符了,+表示至少匹配1次, 所以最后一行没有子串选中。
  5. <a name="UzoQI"></a>
  6. ## 问号-匹配0-1次
  7. ? 表示匹配前面的子表达式0次或1次。 <br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2372430/1649491668863-d75a27c4-682b-4ed6-8f5e-66e1db02e508.png#clientId=ud37f0a74-9d70-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ubfdeb813&margin=%5Bobject%20Object%5D&name=image.png&originHeight=233&originWidth=388&originalType=url&ratio=1&rotation=0&showTitle=false&size=12214&status=done&style=none&taskId=u578d7f11-741c-4c80-9517-ffb9c45852c&title=)<br /> 最后一行,猴子逗号后面没有其它字符了,但是?表示匹配1次或0次, 所以最后一行也选中了一个逗号字符。
  8. <a name="c1Ra3"></a>
  9. ## 花括号-匹配指定次数
  10. 花括号表示 前面的字符匹配 指定的次数 <br />表达式 油{3,4} 就表示匹配 连续的 至少3次,至多 4 <br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2372430/1649491791600-537eadd6-3bce-4ddd-9d82-5b3ee16740c0.png#clientId=ud37f0a74-9d70-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uf6c529d1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=162&originWidth=563&originalType=url&ratio=1&rotation=0&showTitle=false&size=11053&status=done&style=none&taskId=u48e39afc-d9ff-4d21-ad48-7b82dd5eb27&title=)
  11. <a name="X4gZq"></a>
  12. ## 贪婪模式和非贪婪模式
  13. 我们要把下面的字符串中的所有html标签都提取出来,
  14. ```html
  15. source = '<html><head><title>Title</title>'

得到这样的一个列表

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

很容易想到使用正则表达式 <.*>

在正则表达式中, ‘’, ‘+’, ‘?’ 都是贪婪地,使用他们时,会尽可能多的匹配内容,
所以, <.
> 中的 星号(表示任意次数的重复),一直匹配到了 字符串最后的 里面的e。

解决这个问题,就需要使用非贪婪模式,也就是在星号后面加上 ? ,变成这样 <.*?>

对元字符的转义 \的用途

反斜杠 \ 在正则表达式中有多种用途。

.*.,用反斜杠进行转义。

content = '''苹果.是绿色的
橙子.是橙色的
香蕉.是黄色的'''

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

匹配某种字符类型

反斜杠后面接一些字符,表示匹配 某种类型 的一个字符。
比如
\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,.] 表示匹配 : 任何空白字符, 或者逗号,或者点

方括号-匹配几个字符之一

方括号表示要匹配 指定的几个字符之一 。
比如
[abc] 可以匹配 a, b, 或者 c 里面的任意一个字符。等价于 [a-c] 。
[a-c] 中间的 - 表示一个范围从a 到 c。
如果你想匹配所有的小写字母,可以使用 [a-z]

一些 元字符 在 方括号内 失去了魔法, 变得和普通字符一样了。
比如
[akm.] 匹配 a k m . 里面任意一个字符
这里 . 在括号里面不在表示 匹配任意字符了,而就是表示匹配 . 这个 字符

如果在方括号中使用 ^ , 表示 非 方括号里面的字符集合。

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

^ 表示匹配文本的 开头 位置。

正则表达式可以设定 单行模式 和 多行模式
如果是 单行模式 ,表示匹配 整个文本 的开头位置。
如果是 多行模式 ,表示匹配 文本每行 的开头位置。
比如,下面的文本中,每行最前面的数字表示水果的编号,最后的数字表示价格
image.png

content = '''001-苹果价格-60
002-橙子价格-70
003-香蕉价格-80'''

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

注意,compile 的第二个参数 re.M ,指明了使用多行模式。

$ 表示匹配文本的 结尾 位置

如果是 单行模式 ,表示匹配 整个文本 的结尾位置。
如果是 多行模式 ,表示匹配 文本每行 的结尾位置。
比如,下面的文本中,每行最前面的数字表示水果的编号,最后的数字表示价格

竖线-匹配其中之一

image.png
特别要注意的是, 竖线在正则表达式的优先级是最低的, 这就意味着,竖线隔开的部分是一个整体
比如 绿色|橙 表示 要匹配是 绿色 或者 橙 ,
而不是 绿色 或者 绿橙

括号-分组 组选择

括号称之为 正则表达式的 组选择。
组 就是把 正则表达式 匹配的内容 里面 其中的某些部分 标记为某个组。
我们可以在 正则表达式中 标记 多个 组
为什么要有组的概念呢?因为我们往往需要提取已经匹配的 内容里面的 某些部分的信心。
前面,我们有个例子,从下面的文本中,选择每行逗号前面的字符串,也 包括逗号本身 。
苹果,苹果是绿色的 橙子,橙子是橙色的 香蕉,香蕉是黄色的
就可以这样写正则表达式 ^., 。
但是,如果我们要求 不要包括逗号 呢?
当然不能直接 这样写 ^.

因为最后的逗号 是 特征 所在, 如果去掉它,就没法找 逗号前面的了。
但是把逗号放在正则表达式中,又会包含逗号。
解决问题的方法就是使用 组选择符 : 括号。
我们这样写 ^(.*), ,结果如下
image.png
分组 还可以多次使用。
当有多个分组的时候,我们可以使用 (?P<分组名>…) 这样的格式,给每个分组命名。
这样做的好处是,更方便后续的代码提取每个分组里面的内容

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'))
    print(match.group('phone'))

让点匹配换行

前面说过, 点 是 不匹配换行符 的,可是有时候,特征 字符串就是跨行的,比如要找出下面文字中所有的职位名称

<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>

这时你需要 点也匹配换行符 ,可以使用 DOTALL 参数

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)

切割字符串

字符串 对象的 split 方法只适用于 简单的字符串分割。 有时,你需要更加灵活的字符串切割。
比如,我们需要从下面字符串中提取武将的名字。

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

字符串替换

匹配模式替换

字符串 对象的 replace 方法只适应于 简单的 替换。 有时,你需要更加灵活的字符串替换。
比如,我们需要在下面这段文本中 所有的 链接中 找到所以 /avxxxxxx/ 这种 以 /av 开头,后面接一串数字, 这种模式的字符串。
然后,这些字符串全部 替换为 /cn345677/ 。

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>
这节讲的是切割磁力线
'''

被替换的内容不是固定的,所以没法用 字符串的replace方法。
这时,可以使用正则表达式里面的 sub 方法:

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)

sub 方法就是也是替换 字符串, 但是被替换的内容 用 正则表达式来表示 符合特征的所有字符串。
比如,这里就是第一个参数 /av\d+?/ 这个正则表达式,表示以 /av 开头,后面是一串数字,再以 / 结尾的 这种特征的字符串 ,是需要被替换的。
第二个参数,这里 是 ‘/cn345677/‘ 这个字符串,表示用什么来替换。
第三个参数是 源字符串。