[TOC]

回顾昨天的内容

异常处理
try except 一定要在 except 之后写一些提示或者处理的内容

try:
    '''可能会出现异常的代码'''
except ValueError:
    '''打印一些提示或者处理的内容'''
except NameError:
    '''...'''
# except Exception as e:
#     '''打印 e'''
else:
    '''try 中的代码正常执行了'''
finally:
    '''无论错误是否发生,都会执行这段代码,用来做一些收尾工作'''

一、re 模块

re 模块 可以读懂 你写的正则表达式

根据你写的表达式去执行任务

一般网站注册手机,会验证手机号是否有效 根据手机号码一共 11 位并且是只以 13、14、15、18 开头的数字这些特点,我们用 python 写了如下代码:

判断手机号码是否合法 1

while True:
    phone_number = input('please input your phone number: ')
    if len(phone_number) == 11 \
            and phone_number.isdigit()\
            and (phone_number.startswith('13') \
            or phone_number.startswith('14') \
            or phone_number.startswith('15') \
            or phone_number.startswith('18')):
        print('是合法的手机号码')
    else:
        print('不是合法的手机号码')

上面的代码太冗长了

使用正则

import re
phone_number = input('please input your phone number: ')
if re.match('^(13|14|15|18)[0-9]{9}$',phone_number):
        print('是合法的手机号码')
else:
        print('不是合法的手机号码')

假如有一文件

一段文字

fefsfsd13838383838
f13838383838
13838383838ffsfsd
fdsa13838383838et13838383838

需要匹配出手机号码,用 if 就不好处理了,需要使用正则

正则表达式是做什么的?

正则表达式 字符串的操作

使用一些规则来检测字符串是否符合我的要求 —— 表单验证

从一段字符串中找到符合我要求的内容 —— 爬虫

网页的内容,最终也是字符串

正则表达式,是专属字符串操作规则

正则表达式不仅在 python 领域,在整个编程届都占有举足轻重的地位。

正则表达式本身也和 python 没有什么关系,就是匹配字符串内容的一种规则。

官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

正则表达式

在线测试工具 http://tool.chinaz.com/regex/

这个是最好的正则表达式工具,正则可以随时匹配出结果

    ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597887485838-b50827d3-255b-4981-a656-e65cab05ca70.png)      

缺点:

如果只会用这个工具,而不会自己写的话,就不行了。

主要是自己写,不要太依赖它。

匹配一个字符串 a

import re
str1 = 'a'
ret = re.match('a',str1)
print(ret)

执行输出:

<_sre.SRE_Match object; span=(0, 1), match='a'>

结果是一个匹配对象,请注意结尾的 match=’a’ 表示匹配出了 a

如果没有匹配上,结果为 None

打印匹配结果,使用 group()方法查看

import re
str1 = 'a'
ret = re.match('a',str1)
print(ret.group())

执行输出:a

如果没有匹配上,直接使用 group()方法,会报错

AttributeError: ‘NoneType’ object has no attribute ‘group’

所以得配合 if 判断才行

import re
str1 = 'a'
ret = re.match('ab',str1)
if ret:print(ret.group())  # 即是匹配不上,也不会报错

这种情况,是匹配不上的

import re
str1 = 'a1'
ret = re.match('a11',str1)
if ret:print(ret.group())

结论:

完全相等的字符串都可以匹配上

字符组

字符串用[]表示,它只能匹配一个字符串

字符组 : [字符组]
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是 0、1、2...9 这 10 个数之一。
import re
str1 = '2'
ret = re.match('[123abc]',str1)
if ret:print(ret.group())

执行输出:2

import re
str1 = '123'
ret = re.match('[123abc]',str1)
if ret:print(ret.group())

执行输出:1

正则 待匹配字符 匹配
结果
说明
[0123456789] 8 True 在一个字符组里枚举合法的所有字符,字符组里的任意一个字符
和”待匹配字符”相同都视为可以匹配
[0123456789] a False 由于字符组中没有”a”字符,所以不能匹配

[0-9]

7
True 也可以用-表示范围,[0-9]就和[0123456789]是一个意思

[a-z]

s

True

同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示

[A-Z]

B

True

[A-Z]就表示所有的大写字母

[0-9a-fA-F]

e

True

可以匹配数字,大小写形式的 a~f,用来验证十六进制字符

字符:

红色部分是比较常用的

量词:

量词 用法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复 n
{n,} 重复 n 次或更多次
{n,m} 重复 n m

字符在正则表达式中有特殊意义的

[9-0] 是不可以的

import re
str1 = '123'
ret = re.match('[123abc]',str1)
if ret:print(ret.group())

执行报错:

sre_constants.error: bad character range 9-0 at position 1

为啥呢?

re 模块搜查单字符,其字符集合必须按其 ASCII 值(或者说编码值)由小到大排列,否则报错:error: bad character range

[5-9] 这种是可以的

[5.5-9] 这种是不可以的,不允许有小数点

匹配 3 位数字

import re
str1 = '123'
ret = re.match('[1-9][1-9][1-9]',str1)
if ret:print(ret.group())

执行输出:123

第二种写法:

import re
str1 = '123'
ret = re.match('[1-9]{3}',str1)  # {3}表示重复3次
if ret:print(ret.group())

执行输出:123

第三种写法:

import re
str1 = '123'
ret = re.match('\d{3}',str1)  # \d表示匹配数字
if ret:print(ret.group())

执行输出:123

匹配大写

import re
str1 = 'AUE'
ret = re.match('[A-Z]{3}',str1)
if ret:print(ret.group())

执行输出:AUE

匹配大小写

import re
str1 = 'ilikeSHE'
ret = re.match('[A-Za-z]{8}',str1)
if ret:print(ret.group())

执行输出:ilikeSHE

不能写[A-z],因为 A-z 之间的 ASCII 码,不是连续的。中间还有特殊字符,比如[

[0-9a-fA-F] 表示匹配十六进制

总结:

字符组 字符组代表一个字符位置上可以出现的所有内容

范围 :

根据 asc 码来的,范围必须是从小到大的指向

一个字符组中可以有多个范围

. ^ $

正则 待匹配字符 匹配
结果
说明
海. 海燕海娇海东 海燕海娇海东 匹配所有”海.”的字符
^海. 海燕海娇海东 海燕 只从开头匹配”海.”
海.$ 海燕海娇海东 海东 只匹配结尾的”海.$”

* + ? { }

正则 待匹配字符 匹配
结果
说明
李.? 李杰和李莲英和李二棍子 李杰
李莲
李二

?表示重复零次或一次,即只匹配”李”后面一个任意字符
李.* 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子 *表示重复零次或多次,即匹配”李”后面 0 或多个任意字符
李.+ 李杰和李莲英和李二棍子 李杰和李莲英和李二棍子 +表示重复一次或多次,即只匹配”李”后面 1 个或多个任意字符
李.{1,2} 李杰和李莲英和李二棍子 李杰和
李莲英
李二棍
{1,2}匹配 1 到 2 次

注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配

正则 待匹配字符 匹配
结果
说明
李.*? 李杰和李莲英和李二棍子

惰性匹配

正则表示式,不能写在后面。比如海^.

它只能出现在开始位置不能在中间或者后面位置

.*表示匹配所有

惰性匹配

import re
str1 = '李杰和李莲英和李二棍子'
ret = re.match('李.{2,}?',str1)  # 最多2次
if ret:print(ret.group())

执行输出:李杰和

匹配多个数字

import re
str1 = '22675853324354'
ret = re.match('\d+',str1)
if ret:print(ret.group())

执行输出:22675853324354

匹配 11 位以上,不能低于 11 位

import re
str1 = '12345678910111'
ret = re.match('\d{11,}',str1)
if ret:print(ret.group())

执行输出:12345678910111

匹配 11~15 位,如果符合 15 位,优先显示 15

import re
str1 = '12345678910111213'
ret = re.match('\d{11,15}',str1)
if ret:print(ret.group())

执行输出:123456789101112

匹配所有数字

import re
str1 = '12345678910111213'
ret = re.match('\d*',str1)  # *表示零次或者更多次
if ret:print(ret.group())

执行输出:12345678910111213

匹配一位数字

import re
str1 = '1233335446575865'
ret = re.match('\d?',str1)  # ? 重复零次或者一次
if ret:print(ret.group())

执行输出:1

重点:

量词只能约束一个字符组

这里是约束[A-Z]

import re
str1 = '2A32345446'
ret = re.match('\d[A-Z]*',str1)
if ret:print(ret.group())

执行输出:2A

约束\d 和[A-Z]

import re
str1 = '22323A454W46'
ret = re.match('\d*[A-Z]*',str1)
if ret:print(ret.group())

执行输出:22323A

元字符,一般和量词使用

分组 ()与 或 |[^]

身份证号码是一个长度为 15 或 18 个字符的字符串,如果是 15 位则全部由数字组成,首位不能为 0;如果是 18 位,则前 17 位全部是数字,末位可能是数字或 x,下面我们尝试用正则来表示:

步骤分析:

1.匹配非零的,使用 [1-9]

2.匹配 15 位数字,使用 [1-9]\d{14}

3.匹配 18 位数字,使用 [1-9]\d{16}[0-9x],优化成 [1-9]\d{16}[\dx]

4.将 15 位和 18 位的,一并判断,使用| ,规则为 [1-9]\d{16}[\dx]|[1-9]\d{14}

如果两个正则表达式之间用”或”连接,且有一部分正则规则相同,

那么一定要把规则长的放在前面

测试号码:

import re
str1 = '110101198001017'
ret = re.match('[1-9]\d{16}[\dx]|[1-9]\d{14}',str1)
if ret:print(ret.group())

执行输出:110101198001017

再测试一个

import re
str1 = '11010119800101702x'
ret = re.match('[1-9]\d{16}[\dx]|[1-9]\d{14}',str1)
if ret:print(ret.group())

执行输出:11010119800101702x

分组

import re
str1 = '1101011980010172345'
ret = re.match('[1-9]\d{14}(\d{2}[\dx])?',str1)  # 最多匹配18位数字
if ret:print(ret.group())

执行输出:110101198001017234

如果对一组正则表达式整体有一个量词约束,就将这一组表达式分成一个组

在组外进行量词约束

转义符 \

在正则表达式中,有很多有特殊意义的是元字符,比如\d 和\s 等,如果要在正则中匹配正常的”\d”而不是”数字”就需要对”\”进行转义,变成’\‘。

在 python 中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次”\d”,字符串中要写成’\d’,那么正则里就要写成”\\d”,这样就太麻烦了。这个时候我们就用到了 r’\d’这个概念,此时的正则是 r’\d’就可以了。

正则 待匹配字符 匹配
结果
说明
\d \d False 因为在正则表达式中\是有特殊意义的字符,所以要匹配\d 本身,用表达式\d 无法匹配
\d \d True 转义\之后变成\,即可匹配
“\\d” ‘\d’ True 如果在 python 中,字符串中的’\’也需要转义,所以每一个字符串’\’又需要转义一次
r’\d’ r’\d’ True 在字符串之前加 r,让整个字符串不转义

匹配\n,需要使用\n

贪婪匹配

贪婪匹配:在满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配

正则 待匹配字符 匹配
结果
说明
<.*>