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)
介绍
如果给定的 id
或 name
存在,将会尝试匹配 yes-pattern
,否则就尝试匹配 no-pattern
,no-pattern
可选,也可以被忽略。
是不是有点像if else
三目运算,其中 id
和 name
是分组 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
模块各函数的用法,敬请期待......