python 学习笔记 —— 正则表达式

正则表达式

一. 正则表达式概述

1. 正则表达式简介

  • 正则表达式是对 字符串 (包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种 逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个 “规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

2. 正则表达式的作用

  • 判断用户的数据是否符合要求
  • 对带有信息的文件进行信息提取

二. python 中使用正则表达式

1. 基本使用步骤

  • 第一步:导入 re 模块
    import re
    
  • 第二步:使用 match 方法进行匹配
    result = re.match( 正则表达式, 要匹配的字符)
    
  • 如果匹配到数据,可以使用 group 方法提取数据
    result.group()
    

2. re 模块实例

(1) 匹配 hello

  • 代码演示:
    >>> import re #导入 re 模块
    >>> re.match(r"hello", "hello wrold")			# 匹配 hello 
    <re.Match object; span=(0, 5), match='hello'>	
    
  • 从实例中可以看到,我们成功匹配 hello

三. 匹配单个字符

1. 同时匹配 hello 和 Hello

  • 代码演示
    >>> re.match(r"hello", "hello wrold")				# 匹配 hello 
    <re.Match object; span=(0, 5), match='hello'>
    >>> re.match(r"hello", "Hello wrold")			# 匹配 Hello
    
    
  • 从代码可以看到,我们此时只能匹配 hello 不能匹配 Hello
  • 那如何同时匹配 hello 和 Hello 呢?我们只需使用一个 [] 即可
  • 代码演示:
    >>> re.match(r"[Hh]ello", "hello wrold")				# 匹配 hello 
    <re.Match object; span=(0, 5), match='hello'>
    >>> re.match(r"[Hh]ello", "Hello wrold")			# 匹配 Hello
    <re.Match object; span=(0, 5), match='Hello'>
    
  • 可以看到,在 [] 里面添加 Hh,我们就可以同时匹配 hello 和 Hello 了,这就是一种正则表达式的运用

2. 匹配单个字符常用的特殊字符

  • 在正则表达式里有许多的字符用来辅助匹配。下面我就来列举一下,在匹配单个字符常用的特殊字符:
    字符功能
    .匹配任意一个字符
    []匹配 [] 中列举的字符
    \d匹配数字
    \D匹配非数字
    \s匹配空白,即 空格以及 tab 键
    \S匹配非空白
    \w匹配单词字符,即 a-z, A-Z, 0-9,_
    \W匹配非单词字符
  • 从图中可以看出,一般大写字母与小写字母的功能相反。

3. 匹配《速度与激情 》系列

  • 目前,《速度与激情 》已经出到了第八部。今天我们就用《速度与激情》来练习一下怎么使用正则表达式。
  • 首先,我们需要匹配出 速度与激情 字符
    >>>re.match(r"速度与激情", "速度与激情1")
    <re.Match object; span=(0, 5), match='速度与激情'>
    
  • 通过上面的代码,我们已经可以匹配出“速度与激情” 了,那如何匹配后面的 数字 呢?
  • 对于数字匹配,我们一般会想到使用 \d 来进行匹配
    >>>re.match(r"速度与激情\d", "速度与激情1")
    <re.Match object; span=(0, 6), match='速度与激情1'>
    >>>re.match(r"速度与激情\d", "速度与激情5")
    <re.Match object; span=(0, 6), match='速度与激情5'>
    re.match(r"速度与激情\d", "速度与激情9")
    <re.Match object; span=(0, 6), match='速度与激情9'>
    
  • 使用 \d ,已经可以匹配出数字了。但是,在上面我们输入《速度与激情9》也被匹配出来了,这显然不符合我们的要求。
  • 这时,我们可以用 ** [] ** 来限定输入。
    >>>re.match(r"速度与激情[12345678]", "速度与激情1")
    <re.Match object; span=(0, 6), match='速度与激情1'>
    >>>re.match(r"速度与激情[12345678]", "速度与激情5")
    <re.Match object; span=(0, 6), match='速度与激情5'>
    >>>re.match(r"速度与激情[12345678]", "速度与激情9")		# 匹配 《速度与激情9》
    
    
  • 此时,我们我们己经可以限定数字为 1-8 了。但对于这种连续的数字,我们有种更简便的写法
    >>>re.match(r"速度与激情[1-8]", "速度与激情1")
    <re.Match object; span=(0, 6), match='速度与激情1'>
    >>>re.match(r"速度与激情[1-8]", "速度与激情5")
    <re.Match object; span=(0, 6), match='速度与激情5'>
    >>>re.match(r"速度与激情[1-8]", "速度与激情9")
    
    
  • 可以看到,使用 [1-8][12345678] 的效果是一样的。对于这种连续的数字,我们只要使用 - 就可以减少我们的代码量了。
  • 假如我们只想匹配 速度与激情 123 5678 系列 那该怎么去写呢?
    >>>re.match(r"速度与激情[1235678]", "速度与激情1")
    <re.Match object; span=(0, 6), match='速度与激情1'>
    >>>re.match(r"速度与激情[1235678]", "速度与激情5")
    <re.Match object; span=(0, 6), match='速度与激情5'>
    >>>re.match(r"速度与激情[1235678]", "速度与激情4")
    
    
  • 当然,我们也可以假如 - 去实现
    >>>re.match(r"速度与激情[1-35-8]", "速度与激情1")
    <re.Match object; span=(0, 6), match='速度与激情1'>
    >>>re.match(r"速度与激情[1-35-8]", "速度与激情5")
    <re.Match object; span=(0, 6), match='速度与激情5'>
    >>>re.match(r"速度与激情[1-35-8]", "速度与激情4")
    
    
  • 可以看到,两者的效果一样。

四. 匹配多个字符

1. 匹配手机号

  • 目前我们的手机号一般都是 11 位数字,那么我们怎么去指定手机位数呢?
  • 对于指定固定位数,我们可以用 {} 去实现
    >>>re.match(r"\d{11}", "12345678912")			# 匹配 11 位数字
    <re.Match object; span=(0, 11), match='12345678912'>
    >>>re.match(r"\d{11}", "1234567891")		# 只有 10 个数
    
    >>>re.match(r"\d{11}", "1234567d912")		# 中间参杂字母	
    
    
  • 可以看到,使用 \d{11} 我们可以指定数字位有 11 位。其中, {m} 的作用是:匹配前一个字符出现 m 次。

2. 匹配多个字符的相关格式

  • 在匹配多个字符时,我们需要限定前一个字符出现的次数,此时我们就用到一些特殊字符了。
    字符功能
    *匹配前一个字符出现 0 次或者无限次
    +匹配前一个字符出现 1 次或者无限次
    匹配前一个字符出现 1 次或者 0 次
    {m}匹配前一个字符出现 m 次
    {m, n}匹配前一个字符出现 m 到 n 次

3. 匹配固话号码

  • 匹配 区号 3 位 + 号码 8 位
    >>>re.match(r"\d{3}-\d{8}", "020-78787789")
    <re.Match object; span=(0, 12), match='020-78787789'>
    
  • 匹配 区号 4 位 + 号码 7 位
    >>>re.match(r"\d{4}-\d{7}", "0521-7878778")
    <re.Match object; span=(0, 12), match='0521-7878778'>
    
  • 匹配 区号 3 位或 4 位 + 号码 7 位或 8 位
    >>>re.match(r"\d{3,4}-\d{7,8}", "0521-7878778")
    <re.Match object; span=(0, 12), match='0521-7878778'>
    >>>re.match(r"\d{3,4}-\d{7,8}", "020-78787789")
    <re.Match object; span=(0, 12), match='020-78787789'>
    

4. 匹配变量名

  • 在 python 中,变量名第一位可以为字母或下划线,后面可以为字母,下划线或是数字。接下来,我们来练习一下如何写出匹配变量名的正则表达式。
  • 首先,我给出一个变量名列表:names = [“age”, “age1”, “_age”, “age$1”, “1age”]
  • 根据定义变量名的要求,列表中正确的变量名有:age, age1, _age
  • 下面,我们就尝试一下去写出符合变量名的正则表达式来判断上面变量名列表中的元素那些符合要求
  • 分析步骤:
    • 按照变量名的要求,我们可以首先来判断第一位字符是否符合要求,由于第一位为字母或是下划线,所以我们可以的到第一位的正则表达式为:[a-zA-z_]
    • 接着,判断后面的字符。后面的字符可有可无,所以用 * 来匹配前面字符出现的次数。又因为,后面的字符可以为可以为字母,下划线或是数字。所以,正则表达式为: [a-zA-Z0-9_]*
    • 则,最后的正则表达式为:[a-zA-Z_][a-zA-Z0-9_]*
  • 综合的代码为:
    import re
    
    names = ["age", "age1", "_age", "age$1", "1age"]
    
    for name in names:
        ret = re.match(r"[a-zA-Z_][a-zA-Z0-9_]*", name)
        if ret:
            print("变量名:{} 符合要求".format(name))
        else:
            print("变量名:{} 不符合要求".format(name))
    
  • 运行结果为:
    变量名:age 符合要求
    变量名:age1 符合要求
    变量名:_age 符合要求
    变量名:age$1 符合要求
    变量名:1age 不符合要求
    
  • 从运行结果中,我们发现 age$1 也符合要求,这是为什么呢?
  • 原来,此时正则表达式是把符合要求的字符串提取出来了,并没有将整个字符串匹配完。我们可以使用 group() 方法查看匹配的变量名。
    import re
    
    names = ["age", "age1", "_age", "age$1", "1age"]
    
    for name in names:
        ret = re.match(r"[a-zA-Z_][a-zA-Z0-9_]*", name)
        if ret:
            print("变量名:{} 符合要求,匹配的变量名为:{}".format(name, ret.group()))
        else:
            print("变量名:{} 不符合要求".format(name))
    
  • 运行结果:
    变量名:age 符合要求,匹配的变量名为:age
    变量名:age1 符合要求,匹配的变量名为:age1
    变量名:_age 符合要求,匹配的变量名为:_age
    变量名:age$1 符合要求,匹配的变量名为:age
    变量名:1age 不符合要求
    
  • 可以看到,此时 age$1 匹配的变量名为 age ,只是将符合正则表达式的字符匹配出来了,并没有匹配完整个变量名。
  • 那么,怎么可以对字符串进行从头至尾的匹配了。这里,我们就要使用 ^ 以及 $ 符号了。其中, ^ 表示匹配字符串开头, $ 表示匹配字符串结尾。
  • 下面,我们加上 ^ 以及 $ 符号,对字符串进行全部匹配
    import re
    
    names = ["age", "age1", "_age", "age$1", "1age"]
    
    for name in names:
        ret = re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", name)
        if ret:
            print("变量名:{} 符合要求,匹配的变量名为:{}".format(name, ret.group()))
        else:
            print("变量名:{} 不符合要求".format(name))
    
  • 运行结果:
    变量名:age 符合要求,匹配的变量名为:age
    变量名:age1 符合要求,匹配的变量名为:age1
    变量名:_age 符合要求,匹配的变量名为:_age
    变量名:age$1 不符合要求
    变量名:1age 不符合要求
    
  • 可以看到此时已经可以进行变量名的匹配了。
  • 注意由于 re.match() 会自己从头进行匹配,所以我们不需要写 ^

5. 练习

  • 要求:匹配出 163 的邮箱地址,且 @ 符号之间有 4 到 20 位字母,数字或下划线,例如:hell0@163.com

  • 分析:

    • 首先,我们要匹配 4 到 20 位字母,数字或下划线,所以此处的正则表达式为:[a-zA-Z0-9_]{4,20}
    • 然后,由于要从头到尾进行匹配,所以要到末尾加 $ 符号。
    • 所以,整体的正则表达式为:[a-zA-Z0-9_]{4,20}@163.com$
  • 代码演示:

    import re
    
    email_address = input("请输入一个 163 邮箱地址")
    
    ret = re.match("[a-zA-Z0-9_]{4,20}@163.com$", email_address)
    
    if(ret):
        print("{} 邮箱地址地正确".format(ret.group()))
    else:
        print("邮箱地址地错误")
    
  • 运行结果

    1.输入邮箱地址为:123456@163.com
    输出:
    请输入一个 163 邮箱地址123456@163.com
    123456@163.com 邮箱地址地正确
    
    2. 输入邮箱地址为:123@163.com
    输出:
    请输入一个 163 邮箱地址123@163.com
    邮箱地址地错误
    
    3. 输入邮箱地址为:123456@163Acom
    输出:
    请输入一个 163 邮箱地址123456@163Acom
    123456@163Acom 邮箱地址地正确
    
  • 通过上面的几次运行,我们发现当输入 123456@163Acom 地址是,也会匹配成功。

  • 这是由于 . 会匹配任意一个字符,所以当我们想匹配 . 的时候,我们在前添加 \ ,写成 \.

  • 假如,我们想匹配 ?或是 + 等时,也要加上**\**

  • 代码改进

    import re
    
    email_address = input("请输入一个 163 邮箱地址")
    
    ret = re.match("[a-zA-Z0-9_]{4,20}@163\.com$", email_address)
    
    if(ret):
        print("{} 邮箱地址地正确".format(ret.group()))
    else:
        print("邮箱地址地错误")
    
  • 此时再次输入:123456@163Acom 的返回结果为:

    请输入一个 163 邮箱地址123456@163Acom
    邮箱地址地错误
    
  • 此时,我们就圆满完成练习。

五. 匹配分组

  • 练习升级:
    • 在上面的练习中,我们只匹配了 163 邮箱地址。假如在匹配 163 邮箱地址时,还想要同时匹配 QQ 邮箱地址 或是 新浪邮箱地址 该怎么做呢?
  • 这里就涉及到了一个新的知识 匹配分组

1. 匹配分组特殊字符

  • 在完成新的练习前,我们先介绍一下匹配分组里的特殊字符:
    字符功能
    |匹配左右任意一个字符
    (ab)将括号中字符作为一个分组
    \num引用分组 num 匹配到的字符串
    (?P<name>)分组起别名
    (?P=name)引用别名为 name 分组匹配到的字符串

2. 完成练习

  • 由于我们同时要进行 163 邮箱,QQ 邮箱以及新浪邮箱地址的匹配,所以我们必须要用到 | ,在用到 | 时,为了避免歧义,所以还要用到 ()
  • 那么此时的正则表达式应为:[a-zA-Z0-9_]{4,20}@(163|qq|sina).com$
  • 下面,我们就来验证一下:
    >>>re.match(r"[a-zA-Z0-9_]{4,20}@(163|qq|sina)\.com$","123456@qq.com" )
    <re.Match object; span=(0, 13), match='123456@qq.com'>
    >>>re.match(r"[a-zA-Z0-9_]{4,20}@(163|qq|sina)\.com$","123456@sina.com" )
    <re.Match object; span=(0, 15), match='123456@sina.com'>
    >>>re.match(r"[a-zA-Z0-9_]{4,20}@(163|qq|sina)\.com$","123456@163.com" )
    <re.Match object; span=(0, 14), match='123456@163.com'>
    >>>re.match(r"[a-zA-Z0-9_]{4,20}@(163|qq|sina)\.com$","123456@163sina.com" )
    
    
  • 可以看到,现在已经能够实现 3 个邮箱地址的匹配了。
  • 而且,通过分组,我们可以把各个分组的内容提取出来。假如,我们想将邮箱前面的号码以及是属于那么公司的邮箱提取出来,这时,我们就能通过分组提取了。
    a = re.match(r"([a-zA-Z0-9_]{4,20})@(163|qq|sina)\.com$","123456@163.com" )
    print(a.group(1))
    print(a.group(2))
    
  • 运行结果:
    123456
    163
    
  • 可以看到,通过对应的分组号,我们就能提取分组里面的内容了。

3. 网页匹配应用

  • 在网页文件里面,我们可以看到很多标签组,比如:<body><h1>hahahahahha</h1></body>。对于完成这种一些字符必须成对出现,我们就可以运用分组的另一个功能了。
  • 假如我们想匹配:<body><h1>hahahahahha</h1></body> ,这种里面有一些字符必须成对出现的,我们可以这样来做。
    >>>html_content = "<body><h1>hahahahahha</h1></body>"
    >>>re.match(r"<(\w*)><(\w*)>.*<(/\2)><(/\1)>", html_content)
    <re.Match object; span=(0, 33), match='<body><h1>hahahahahha</h1></body>'>
    
  • 其中,\1 代表必须与分组 1 的内容完成相同才能完成匹配,\2 代表必须与分组 2 的内容完成相同才能完成匹配。
  • 另外,我们也可以通过起别名的方式来进行匹配:
    >>>html_content = "<body><h1>hahahahahha</h1></body>"
    >>>re.match(r"<(?P<p1>\w*)><(?P<p2>\w*)>.*</(?P=p2)></(?P=p1)>", html_content)
    <re.Match object; span=(0, 33), match='<body><h1>hahahahahha</h1></body>'>
    
  • 可以看到,我们可以通过 (?P<name>) 来为分组取别名,然后使用 (?P=name) 来引用别名进行匹配。

六. 结语

  • 本文主要根据黑马程序员的 python 视频编写而成。
  • 如果文中有写的不对的地方,请大家指正。联系方式:lwl510ll@163.com
  • 本文到这就结束了,感谢大家的观看。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值