标准库之正则表达式3-前后向管理

本文介绍如何使用Python的re模块编写易于理解的正则表达式,并通过re.VERBOSE选项来增强可读性。文中提供了多种高级功能示例,包括前向与后向断言、自引用管理和逻辑判断管理等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

更加易读的正则表达式

Python如何写出比较详细易读的正则表达式?
需要借助一个选项: re.VERBOSE
观察如下的三个版本:
第一版本,复杂,不易读,不好改。

#  用来匹配邮箱地址 : first.last@example.com
re.compile('[\w\d.+-]+@([\w\d.]+\.)+(com|org|edu)')

第二版本, 容易读,功能和第一版本相同:

re.compile(
        '''
        [\w\d.+-]+       # 邮箱前缀
        @
        ([\w\d.]+\.)+    # 域名
        (com|org|edu)    # 顶级域名
        ''',
        re.VERBOSE)

使用re.VERBOSE 构建正则表达式:

  • 用换行分割匹配的各个部分。
  • 可以使用注释让正则更加清晰。
  • 使用前后项管理、自引用或逻辑的管理更加高级的功能。

高级功能

前向和后向断言。

对某处前边或后边的模式进行预设定,能匹配的是符合模式的内容。(不占用符号)

模式含义
(?=pattern)肯定前向断言
(?!pattern)否定后向断言
(?<!pattern)肯定后向断言

自引用管理

在模式中匹配前面已经匹配的内容,使用变量引用前面匹配的内容
有num和命名两种方式

模式含义
\num自引用表达式 做多支持两位99个
(?P=name)自引用 指定的一个命名组的值 key-value的形式

逻辑判断管理:

(?(id)yes-expression|no-expression) 查看id的组是否匹配 如果匹配就用 yes-expression
来匹配后面的内容 否则 no-expression 来匹配后面的内容。类似if else.
if 的条件是前面某个id是否匹配。

高级功能Demo测试

前向断言:

address = re.compile(
    '''
    # 
    ((?P<name>
       [\w.,]+
     )
     \s+
    )
    # 前向管理 肯定前向断言 断言两种形式 包含
    (?= (<.*>$)       # 包含尖角号
        |             # 或
        ([^<].*[^>]$) # 不包含尖角号
      )

    <? # 前后向断言不消耗符号  这里可选的匹配尖角号

    # jie.zhang@example.com>
    (?P<email>
      [\w\d.+-]+       # 用户名
      @
      ([\w\d.]+\.)+    # 域名
      (com|org|edu)
    )

    >? # 前后向断言不消耗符号  这里可选的匹配尖角号
    ''',
    re.VERBOSE)     # re.VERBOSE 构建详细内容的正则表达式

candidates = [
    u'张杰 <jie.zhang@example.com>',  # 正例
    u'周杰伦 jielun.zhou@example.com',  # 正例
    u'张杰 <jie.zhang@example.com',  # 反例
    u'周杰伦 jielun.zhou@example.com>',   # 反例
]

for candidate in candidates:
    print('例子:', candidate)
    match = address.search(candidate)
    if match:
        print('  Name :', match.groupdict()['name'])
        print('  Email:', match.groupdict()['email'])
    else:
        print('  No match')

输出:
前向
否定后项断言:

address = re.compile(
    '''
    ^
    # 否定后向断言  这个断言的内容 不能匹配到
    (?!未知@.*$)

    [\w\d.+-]+       # 用户名
    @
    ([\w\d.]+\.)+    # 域名
    (com|org|edu)
    $
    ''',
    re.VERBOSE)

candidates = [
    u'jie.zhang@example.com',
    u'未知@example.com',
]

for candidate in candidates:
    print('例子:', candidate)
    match = address.search(candidate)
    if match:
        print('  Match:', candidate[match.start():match.end()])
    else:
        print('  No match')

输出:
后向
肯定后向断言:

# 肯定后向断言
twitter = re.compile(
    '''
    # 后面的匹配项中  匹配到@开头的
    (?<=@)
    ([\w\d_]+)       # 字母数字下划线组合
    ''',
    re.VERBOSE)

text = '''This text includes two Twitter handles.
One for @ThePSF, and one for the author, @doughellmann. thank@you
'''

print(text)
for match in twitter.findall(text):
    print('Handle:', match)

输出:
肯定后向
\num 形式的自引用表达式 最多支持两位99个----> 也是该模式的一种限制
代码:

address = re.compile(
    r'''

    # First Last <first.last@example.com>
    (\w+)               # First
    \s+
    (([\w.]+)\s+)?      # optional middle name or initial
    (\w+)               # Last

    \s+

    <

    # The address: first_name.last_name@domain.tld
    (?P<email>
      \1               # First
      \.
      \4               # Last
      @
      ([\w\d.]+\.)+
      (com|org|edu)
    )

    >
    ''',
    re.VERBOSE | re.IGNORECASE)   # re.VERBOSE 构建详细内容的正则表达式 和忽略大小写

candidates = [
    u'First Last <first.last@example.com>',
    u'Different Name <first.last@example.com>',
    u'First Middle Last <first.last@example.com>',
    u'First M. Last <first.last@example.com>',
]

for candidate in candidates:
    print('Candidate:', candidate)
    match = address.search(candidate)
    if match:
        print('  Match name :', match.group(1), match.group(4))
        print('  Match email:', match.group(5))
    else:
        print('  No match')

输出:
num
自引用 指定的一个命名组的值 key-value的形式:

address = re.compile(
    '''

    # First Last <first.last@example.com>
    (?P<first_name>\w+) # First
    \s+
    (([\w.]+)\s+)?
    (?P<last_name>\w+) # Last

    \s+

    <

    # The address: first_name.last_name@domain.tld
    (?P<email>
      (?P=first_name) # 更加的直观
      \.
      (?P=last_name)
      @
      ([\w\d.]+\.)+
      (com|org|edu)
    )

    >
    ''',
    re.VERBOSE | re.IGNORECASE)

candidates = [
    u'First Last <first.last@example.com>',  # 正例
    u'Different Name <first.last@example.com>'  # 反例
]

for candidate in candidates:
    print('Candidate:', candidate)
    match = address.search(candidate)
    if match:
        print('  Match name :', match.groupdict()['first_name'],
              end=' ')
        print(match.groupdict()['last_name'])
        print('  Match email:', match.groupdict()['email'])
    else:
        print('  No match')

输出:

groupname
使用命名组的形式,使用上文匹配模式更加的简洁方便,而且没有99个数量上的限制。
(?(id)yes-expression|no-expression) 根据前面某一个是否匹配,来选择后面的使用哪种模式。

例子的逻辑:
没有姓名标注的邮件 不用加尖角号, 有姓名标注的邮件 需要加尖角号

例子代码:

address = re.compile(
    '''
    ^

    # 命名组 name
    (?P<name>
       ([\w.]+\s+)*[\w.]+
     )?
    \s*

    # 判断命名组 name 是否匹配
    (?(name)
      # 匹配上执行的模式  同时监控brackets 命名组是否匹配上 否则任意空白
      (?P<brackets>(?=(<.*>$)))  # 肯定前向断言
      |
      # 未匹配上执行的模式
      (?=([^<].*[^>]$))   # 肯定前向断言
     )

    # brackets 命名组匹配上 加上< 尖角好的占位
    (?(brackets)<?|\s*)

    # The address itself: username@domain.tld
    (?P<email>
      [\w\d.+-]+       # username
      @
      ([\w\d.]+\.)+    # domain name prefix
      (com|org|edu)    # limit the allowed top-level domains
     )

    # brackets 命名组匹配上 加上> 尖角好的占位  否则任意空白
    (?(brackets)>?|\s*)

    $
    ''',
    re.VERBOSE)

candidates = [
    u'First Last <first.last@example.com>',
    u'No Brackets first.last@example.com',
    u'Open Bracket <first.last@example.com',
    u'Close Bracket first.last@example.com>',
    u'no.brackets@example.com',
]

for candidate in candidates:
    print('例子:', candidate)
    match = address.search(candidate)
    if match:
        print('  Match name :', match.groupdict()['name'])
        print('  Match email:', match.groupdict()['email'])
    else:
        print('  No match')

输出为:
ifelse

部分例子引用: https://pymotw.com/3/re/index.html

希望给读者能带来更直接的思考和吸收!很多时候我们不是缺少某种知识本身,我们只是对某类知识缺少勇气和模型的抽象。我们一起勇往直前。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值