python中正则表达式re

概述

在python中通过内置的re库来使用正则表达式,它提供了所有正则表达式的功能。

模式字符串使用特殊的语法来表示一个正则表达式:

字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。

多数字母和数字前加一个反斜杠时会拥有不同的含义。

标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。

反斜杠本身需要使用反斜杠转义。

由于正则表达式通常都包含反斜杠,所有你最后使用原始字符串来表示它们。模式元素(r“t”等价于”\\t”)匹配相应的特殊字符。

正则表达式的4大功能

平时用正则表达式处理字符串的时候,主要会使用到它的四大功能:

  1. 匹配:就是看这个字符串是否符合正则表达式的语法。

  1. 提取:这个功能就是用来通过正则表达式提取指定字符串中符合要求的文本,如果有符合表达式的内容会返回一个或多个。

  1. 替换:这个功能可能用的会少一些,主要是用来将查找到的符合要求的文本,替换成我们指定的字符串。

  1. 拆分:这个功能用的更少了,其实就是用正则表达式对符合要求的文本字串进行分割。

原始字符串r-string

概念

原始字符串(raw string)是所有的字符串都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符,通常简称为 r-string。

转义的问题

正则表达式中用\表示转义,而python中 也用\表示转义,当遇到特殊字符需要转义时,你要花费心思到底需要几个\,所以为了避免这个情况,强烈推荐使用原生字符串类型(raw string)来书写正则表达式。

方法很简单,只需要在表达式前面加个r即可,如下所示:

r'\d{2}-\d{8}'
r'\bt\w*\b'

Re库的常用字符功能

元字符(meta character)就是一种自带特殊含义的字符,也叫特殊字符。

字符类
数量限定符
位置相关
分组

Re库的元字符实例

字符匹配
字符类
特殊字符类
综合

Re库的断言

在实际使用中,我们需要对正则表达式使用断言,来对文本进行部分约束,但断言不会匹配任何文本

  • \b:匹配单词的边界

  • \B:匹配非单词边界,会受到ASCII标记影响

前后查找(lookaround)对某一位置的前、后内容进行查找。

正前瞻、负前瞻(前向界定符包括前向肯定符前项否定界定符):

  • 正前瞻(?=e):如 表达式1(?=表达式2) ,则表达式1后面的内容需要匹配表达式2;

  • 负前瞻(?!e):如 表达式1(?!表达式2) ,则表达式1后面的内容不能匹配表达式2;

正回顾、负回顾(后向界定符包括后向肯定符后项否定界定符):

  • 正回顾(?<=e):如 (?<=表达式2)表达式1 ,则表达式1前面的内容需要匹配表达式2;

  • 负回顾(?<!e):如 (?<!表达式2)表达式1 ,则表达式1前面的内容不能匹配表达式2。

有时候需要匹配一个跟在特定内容后面的或者在特定内容前面的字符串,Python提供一个简便的前向界定和后向界定功能,或者叫前导指定和跟从指定功能

零宽界定符

\b

单词边界。这个是零宽界定符(zero-width assertions)只用以匹配单词的词首和词尾。单词被定义为一个字母数字序列,因此词尾就是用空白符或非字母数字符来标示的。

\b匹配的是这样一个位置,这个位置位于一个能够用来构成单词的字符(字母数字下划线)和一个不能用来构成单词的字符之间。

\b只匹配位置不匹配字符,例如\bchar\b最后的匹配结果只有char这4字符

\B

另一个零宽界定符(zero-width assertions),它正好同\b相反,只在当前位置不在单词边界时匹配。

例子
"""
\b即字母数字下划线 与 非字母数字下划线 之间的位置,此位置不匹配任何字符
也就是匹配单词边界
\B即字母数字下划线 与 字母数字下划线 之间的位置,此位置不匹配任何字符
也就是不匹配单词边界
"""

text = "Please enter the nine-digit id as it appears on your color - coded pass-key."

res = re.findall(r'\B-\B', text)
print(">>>")
print('findall res:', res)
print(text[55:63])

res = re.findall(r'\b-\b', text)
print(">>>")
print('findall res:', res)
前向界定符

(?=…)

前向肯定界定符。如果所含正则表达式,以…表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。

(?!...)

前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功。

例子
import re


if __name__ == '__main__':

    """
    (?=文本A) : 表示查找文本A前面的内容  
    """

    text = 'http://www.forta.com/\n\
    https://mail.forta.com/\n\
    ftp://ftp.forta.com/\n'

    res = re.findall(r".+(?=:)", text)
    print('findall res:', res)

    # findall res: ['http', '    https', '    ftp']
后向界定符

(?<!pattern)后向否定断言语法,表示否定结尾

前面的内容需要不匹配该pattern模式才匹配成功

(?<!pattern)后向肯定断言语法

需要匹配pattern模式才能匹配成功,表示肯定后面的字符内容

例子
import re


if __name__ == '__main__':

    """
    (?<=文本A) : 表示查找文本A后面的内容  
    """

    text = 'ABC01: $23.45\n\
    HGG42: $5.31\n\
    CFMX1: $899.00 XTC99: $69.96\n\
    Total items found: 4'

    res = re.findall(r"(?<=\$)[0-9.]+", text)
    print('findall res:', res)

    # findall res: ['23.45', '5.31', '899.00', '69.96']

代码示例

例如我们想查找一个字串 python,而这个字串后面必须要跟着 data 字串,那我们就可以使用 (python)\s+(?=data)。

这样即使字串中有 python,但我们限定了只匹配这种情况,可以大大提高了准确率。

Re库的分组

分组概述

一般用于获取()中的正则内容进行下一步操作使用,如果想要关闭这种获取能力,可以在左括号(后面加上?:就可以了。

无捕获组

有时你想用一个组去收集正则表达式的一部分,但又对组的内容不感兴趣。你可以用一个无捕获组:(?:…)来实现这项功能,这样你可以在括号中发送任何其他正则表达式。

(?:…)匹配到的括号内字符串不作为分组。

参见代码示例

无命名分组(通过组号访问)

无命名分组:'('')'

最基本的组是由一对圆括号括起来的正则式。

当获取了小括号里的内容后,我们也可以反向引用 ()里的内容,每一个小括号里的内容,只要没有使用 ?: ,就会被分配一个组号,从左到右从1开始递增。

在使用中,可以使用 \i 引用前面 () 里获取的内容,i为数字编号。

'\<number>'引用编号为<number>的分组匹配到字符串。

举例:

s = '<div><ahref="https://support.google.com/chrome/?p=ui_hotword_search"target="_blank">更多</a><p>dfsl</p></div>'
re.search(r'<a.*>(.*)</a>', s).group(1)

或者

re.search(r'<a.*>(.*)</a>', s).groups()
命名分组(通过组名访问)

命名分组:'(?P<name>...)'

'(?P'代表这是一个Python的语法扩展'<name>'里面是你给这个组起的名字,比如你可以给一个全部数字组成的组叫做'num',它的形式就是'(?P<num>\d+)'

调用已匹配的命名分组:'(?P=name) '

要注意,再次调用的这个组是已被匹配的组,也就是说它里面的内容是和前面命名分组的内容是一样的

通过左括号 ( 后面跟一个字串 ?P<组名> 的方式,来为这个组起一个别名,后面就可以通过 (?P=组名) 来引用这个组获取的内容了。例如想要匹配重复数字,可以使用 (?P<key>\d+)\s+(?P=key)。

s = '<h1>hello</h1>'
ret = re.search('<(?P<name>\w+)>\w+</(?P=name)>', s)
print(ret['name'])  # 利用指定命名来取值
代码示例
import re

if __name__ == '__main__':

    p = r'(?:\d{3,4})-(?P<area_code>\d{3,4})-(?P<phone_code>\d{7,8})'
    m = re.search(p, '567-010-87654321')

    print(">>>")
    print(m)
    print(m.group())  # 返回匹配字符串
  print(m.groups())  # 获得所有组内容
  print()

    # 通过组编号返回组内容
  print(">>>")
    print(m.group(1))
    print(m.group(2))
    print()

    # 通过组名返回组内容
  print(">>>")
    print(m.group('area_code'))
    print(m.group('phone_code'))
    print()

Re库的条件表达式

概述

语法:

(?(判断表达式) 为真时执行表达式| 为假时执行表达式)

说明:

当判断表达式匹配到内容的时候,则这里就会匹配为真时的表达式,否则就会匹配为假时的表达式。

例子:

(?(\d+) [\s\d,]| [\s,])
(?(backreference)true-regex)
"""
(?(backreference)true-regex)
其中?表明这是一个条件,
括号里的backreference是一个回溯引用,
true-regex是一个只在backreference存在时才会被执行的子表达式
"""

text='<!-- Nav bar -->\n\
<TD>\n\
<A HREF=”/home”><IMG SRC=”/images/home.gif”></A>\n\
<IMG SRC=”/images/spacer.gif”>\n\
<A HREF=”/search”><IMG SRC=”/images/search.gif”></A>\n\
<IMG SRC=”/images/spacer.gif”>\n\
<A HREF=”/help”><IMG SRC=”/images/help.gif”></A> </TD>'

res = re.findall(r"(<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>(?(1)\s*</[Aa]>)",text)

print('findall res:', res)

# findall res: ['<A HREF=”/home”>', '', '<A HREF=”/search”>', '', '<A HREF=”/help”>']
(?(backreference)true-regex|false-regex)
"""
(?(backreference)true-regex|false-regex)
其中false-regex表示backreference的条件不满足时执行的表达式
"""

text = '123-456-7890\n\
(123)456-7890\n\
(123)-456-7890\n\
(123-456-7890\n\
1234567890\n\
123 456 7890'

res = re.findall(r"(\()?\d{3}(?(1)\)|-)\d{3}-\d{4}", text)
print('findall res:', res)

# findall res: ['', '(', '']

Re库的两种用法

等价方法
函数式用法
对象式用法

Re库常用的功能函数

概述
标志位flags

正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志,如 re.I | re.M 被同时设置成 I 和 M 标志:

re.match()

从字符串的起始位置匹配,匹配成功,返回一个匹配的对象,否则返回None

语法:re.match(pattern, string, flags=0)

pattern: 匹配的正则表达式

string:要匹配的字符串

flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等;flags=0表示不进行特殊指定

可选标志如下:

修饰符被指定为一个可选的标志。多个标志可以通过按位OR(|)它们来指定。如re.I | re.M被设置成I和M标志

例子:

re.search()
re.match与re.search的区别
re.findall()

re.findall() 函数可以遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表。

def findall(pattern, string, flags=0):

第一个参数,正则表达式

第二个参数,搜索的是那些字符串

第三个参数,匹配的模式,其中re.S使匹配包括换行在内的所有字符。

对象式:

import re
regex = re.compile(r'\d+')
res_list = regex.findall('one1two2three3four4')
#[1,2,3,4]

函数式:

res_list = re.findall(r'\d+',"one123")
#[1,2,3]
re.split()
re.finditer()
re.sub()

Re库Match对象

Match对象概述
Match对象在分组时的方法
  • group():返回编号或组名匹配到的内容。默认(0)是表示匹配整个表达式内容,当然也可以指定多个,则会返回一个元组数据。

  • groupdict():这个方法会返回一个字典,其中字典的 KEY 是命名的组名,VALUE 为组名对应获取的内容。

  • groups():该方法会返回一个包含所有捕获内容的子分组的元组,如果指定了默认值,则这个值就会作为没有获取到的内容的值。

  • lastgroup():该方法用于获取最后一个匹配到内容的组名。

  • lastindex():该方法会返回最后一个匹配到内容的组编号。

常用正则表达式

常用
  1. 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$

  1. 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$

  1. Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

  1. 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$

  1. 身份证号码:(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)

  1. 空白行:\n\s*\r

  1. 日期格式:^\d{4}-\d{1,2}-\d{1,2}

  1. 复杂密码:^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{8,10}$

  1. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?

密码

密码通常有如下要求。

  • 最少 8 个最多 16个字符.

  • 至少含有一个大写字母,一个小写字母,一个数字

  • 至少含有一个特殊字符 @ $ ! % * ? & _,但不包括空格

这时候需要用 (?=...) 这个骚操作,意思就是匹配’…’ 之前的字符串。在本例中 '...' 包括小写 [a-z],大写 [A-Z],数字 \d,特殊字符 [@$!%*?&_],言下之义就是上面这些必须包含中密码中。

import re

def look_for(pat, str):
    return '没有找到' if re.search(pat, str) is None else re.findall(pat, str)
    # return re.findall(pat, str)


if __name__ == '__main__':
    """
    密码通常有如下要求。
        最少 8 个最多 16 个字符.
        至少含有一个大写字母,一个小写字母,一个数字
        至少含有一个特殊字符 @ $ ! % * ? & _,但不包括空格
    """

    pat = r"^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[$@$!%*?&_])[A-Za-z\d$@$!%*?&_]{4,16}$"
    # pat = r"^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9]{4,16}$"

    print(look_for(pat, 'stevenwang'))
    print(look_for(pat, '19831031'))
    print(look_for(pat, 'steven1031'))
    print(look_for(pat, 'steven@1031'))
    print(look_for(pat, 'Steven@1031'))
    print(look_for(pat, 'St1031'))
    print(look_for(pat, 's@1031'))
    print(look_for(pat, 'stevenwang19831031'))
    print(look_for(pat, 'stevenwang@19831031'))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wugou2014

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值