Python正则表达式(二)

回顾

上节我们说到Python正则表达式的基本字符,以及这些字符的用法

今天,我们继续讲讲Python中一些扩展标记法,以及一些特殊序列

扩展标记法

(?...): 这种扩展标记法以括号内?开头,其后第一个字符决定了采用什么样的语法。

1、(?aiLmsux)

介绍

?后面添加( 'a', 'i', 'L', 'm', 's', 'u', 'x' 中的一个或多个),然后加上匹配规则。

这些字符对正则表达式设置以下标记,免去设置 flag 参数

'a' ==> re.A(re.ASCII) ==> 只匹配 ASCII 字符
'i' ==> re.I(re.IGNORECASE) ==> 忽略大小写
'L' ==> re.L(re.LOCALE) ==> 由当前语言区域决定 \w, \W, \b, \B 和大小写敏感匹配,不推荐使用。
'm' ==> re.M(re.MULTILINE) ==> 多行模式
's' ==> re.S(re.DOTALL) ==> .匹配全部字符
'u' ==> re.U ==> Unicode匹配,Python3默认开启这个模式
'x' ==> re.X(re.VERBOSE) ==> 冗长模式

注意'a', 'L', 'u' 作为内联标记是相互排斥的,它们不能结合在一起

示例
# 忽略大小写
re.findall('(?i)ab', 'Ab')
# out: ['Ab']

# 连用s、i
re.findall('(?si)ab.', 'Ab\n')
# out: ['Ab']

# 多行模式
re.findall('^a.', 'ab\nac')
# out: ['ab']
re.findall('(?m)^a.', 'ab\nac')
# out: ['ab', 'ac']

# .匹配全部字符
re.findall('(?s)ab.', 'ab\n')
# out: ['ab\n']

# 冗长模式
# 这个标记允许你编写更具可读性更友好的正则表达式。
# 通过分段和添加注释,其中空白符号会被忽略
re.findall(r"""(?x)\d +  # 整数位
                \.       # 小数点
                \d *     # 小数位
                """, '3.1415na')
# out: ['3.1415']

2、(?:…)

介绍

括号分组的非捕获版本,该分组所匹配的子字符串 不能 在执行匹配后被获取或是在之后的模式中被引用

可以配合 |{m} 使用

示例
re.findall('(abc){2}', 'abcabc')
# out: ['abc']

re.findall('(?:abc){2}', 'abcabc')
# out: ['abcabc']

# 可以看出,捕获版本和非捕获版本的区别
# 捕获版本会捕获到()分组内的匹配字符
# 非捕获版本会将()分组内的字符与外面的字符作为一个整体返回
# 看一个嵌套捕获的例子
re.findall('(a(bc))cbs', 'abccbs')
# out: [('abc', 'bc')]

re.findall('(a(?:bc))cbs', 'abccbs')
# out: ['abc']

re.findall('(abc)|cbs', 'cbs')
# out: ['']

re.findall('(?:abc)|cbs', 'cbs')
# out: ['cbs']

3、(?P<name>…)(?P=name)

介绍
  • (?P<name>…)

为分组再指定一个组合名

每个组合名只能用一个正则表达式定义,只能定义一次

  • (?P=name)

反向引用一个命名组合

匹配前面那个名字叫 name 的命名组中匹配到的字符串

示例
re.findall('(?P<name>abc)\\1', 'abcabc')
re.findall('(?P<name>abc)(?P=name)', 'abcabc')
# out: ['abc']

4、(?#…)

介绍

注释信息,里面的内容会被忽略。

示例
re.findall('abc(?#这是注释)123', 'abc123')
# out: ['abc123']

5、(?=…), (?!…)

介绍
  • (?=…):匹配 的内容。这个叫 lookahead assertion (后视断言)
  • (?!…):匹配 不符合的情况。这个叫 negative lookahead assertion(前视取反)

哈哈,是不是没看懂,没事,举个栗子

示例
re.findall('Isaac (?=Asimov)', 'Isaac Asimov, Isaac Ash')
# out: ['Isaac ']
# 只有后面是 'Asimov' 的时候才匹配前面的 'Isaac '

re.findall('Isaac. (?!Asimov)', 'Isaac1 Asimov, Isaac2 Ash')
# out: ['Isaac2 ']
# 为了显示区别,我们加了 '.' 匹配数字 1、2
# 从中可以看出,只有后面 不 是 'Asimov' 的时候才匹配 'Isaac ' 

看看,是不是一下子就明了了。

6、(?<=…), (?<?…)

介绍
  • 匹配当前位置之前是 ... 的样式。这个叫 positive lookbehind assertion (正向后视断定)
  • 匹配当前位置之前不是 ... 的样式。这个叫 negative lookbehind assertion (后视取反)

哈哈,这个又看不懂?

思考一下,既然有根据后面字符断言的,那么根据前面字符来断言,也是很合理的,

示例
re.findall('(?<=Isaac )Asimov.', 'Isaac Asimov1, Asimov2')
# out: ['Asimov1']

re.findall('(?<!Isaac )Asimov.', 'Isaac Asimov1, Asimov2')
# out: ['Asimov2']

7、(?(id/name)yes-pattern|no-pattern)

介绍

如果给定的 idname 存在,将会尝试匹配 yes-pattern,否则就尝试匹配 no-patternno-pattern 可选,也可以被忽略。

是不是有点像if else三目运算,其中 idname 是分组 id、和指定的分组名 name

照旧,举个栗子吧

示例
re.findall('(<)?(\w+@\w+(?:\.\w+))(?(1)>|$)', '<username@host.com>')
# out: [('<', 'username@host.com')]

re.findall('(<)?(\w+@\w+(?:\.\w+))(?(1)>|$)', 'username@host.com>')
# out: []

re.findall('(<)?(\w+@\w+(?:\.\w+))(?(1)>|$)', '<username@host.com')
# out: [('', 'username@host.com')]

re.findall('(<)?(\w+@\w+(?:\.\w+))(?(1)>|$)', 'username@host.com')
# out: [('', 'username@host.com')]

看了栗子是不是有点糊涂呢,我们来解析一下这个正则表达式

  • 第一个括号捕获的是 <
  • ? 是判断 < 是否存在
  • 第二个括号,里面是邮箱的格式,\w 代表数字、字母和下换线集合
  • 第三个括号嵌套在第二个当中,而且声明非捕获版本,是邮箱 . 及后面的字符
  • 最后一个括号当中,?(1)>|$:其中1 是对第一个括号分组的引用,如果存在,就匹配 >,否则匹配空

其结果匹配的就是<username@host.com>username@host.com

而不会匹配 <user@host.com’ 和 <user@host.com

但是上面的第三个结果为啥不一样呢?

因为findall允许返回空匹配的,在有 ?的情况下,所以它会分两种情况去匹配

  • <存在的情况下,匹配不到 >
  • <不存在时,能匹配到 username@host.com

特殊序列

字符描述

image.png

简单示例
re.findall('(ab)c\\1', 'abcab')
# out: ['ab']
# 注意,这里需要 \\,等同于 r'(ab)c\1'
# 为了方便,我们下面都使用 r''

re.findall(r'\Aabc', 'abccadc\nabc')
re.findall(r'^abc', 'abccadc\nabc')
# out: ['abc']
# 只有开头的 abc 匹配了

re.findall(r'\bHello\b', 'Hello world! Hellooo')
# out: ['Hello']
# 注意,通常 \b 定义为 \w 和 \W 字符之间,或者 \w 和字符串开始/结尾的边界

re.findall(r'\bHello\b', 'Hello world! Hello.')
# out: ['Hello', 'Hello']

re.findall(r'\BHello\B', 'Hello worldHello123')
# out: ['Hello']
# Hello 两边都需要 \b 未定义的分隔字符

re.findall(r'\d+', 'ab123d\nabc')
# out: ['123']

re.findall(r'\D+', 'ab123d\nabc')
# out: ['ab', 'd\nabc']

re.findall(r'\s+', 'ab12 3d\nab\tc')
# out: [' ', '\n', '\t']

re.findall(r'\S+', 'ab12 3d\nab\tc')
# out: ['ab12', '3d', 'ab', 'c']

re.findall(r'\w+', 'user_name@host163.com')
# out: ['user_name', 'host163', 'com']

re.findall(r'\W+', 'user_name@host163.com')
# out: ['@', '.']

re.findall(r'dd\Z', 'abddacdd')
re.findall(r'dd$', 'abddacdd')
# out: ['dd']

总结

今天讲了一些扩展标记法,其实没那么难,多看看例子,多练习练习。

下节将介绍 re 模块各函数的用法,敬请期待…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

名本无名

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

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

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

打赏作者

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

抵扣说明:

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

余额充值