回顧
上節(jié)我們說到Python正則表達式的基本字符,以及這些字符的用法
今天,我們繼續(xù)講講Python中一些擴展標(biāo)記法,以及一些特殊序列
擴展標(biāo)記法
(?...): 這種擴展標(biāo)記法以括號內(nèi)?開頭,其后第一個字符決定了采用什么樣的語法。
1、(?aiLmsux)
介紹
在?后面添加( 'a', 'i', 'L', 'm', 's', 'u', 'x'中的一個或多個),然后加上匹配規(guī)則。
這些字符對正則表達式設(shè)置以下標(biāo)記,免去設(shè)置 flag 參數(shù)
'a' ==> re.A(re.ASCII) ==> 只匹配 ASCII 字符
'i' ==> re.I(re.IGNORECASE) ==> 忽略大小寫
'L' ==> re.L(re.LOCALE) ==> 由當(dāng)前語言區(qū)域決定 \w, \W, \b, \B 和大小寫敏感匹配,不推薦使用。
'm' ==> re.M(re.MULTILINE) ==> 多行模式
's' ==> re.S(re.DOTALL) ==> .匹配全部字符
'u' ==> re.U ==> Unicode匹配,Python3默認(rèn)開啟這個模式
'x' ==> re.X(re.VERBOSE) ==> 冗長模式
注意:'a', 'L', 'u' 作為內(nèi)聯(lián)標(biāo)記是相互排斥的,它們不能結(jié)合在一起
示例
# 忽略大小寫
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']
# 冗長模式
# 這個標(biāo)記允許你編寫更具可讀性更友好的正則表達式。
# 通過分段和添加注釋,其中空白符號會被忽略
re.findall(r"""(?x)\d + # 整數(shù)位
\. # 小數(shù)點
\d * # 小數(shù)位
""", '3.1415na')
# out: ['3.1415']
2、(?:…)
介紹
括號分組的非捕獲版本,該分組所匹配的子字符串 不能 在執(zhí)行匹配后被獲取或是在之后的模式中被引用
可以配合 | 和 {m} 使用
示例
re.findall('(abc){2}', 'abcabc')
# out: ['abc']
re.findall('(?:abc){2}', 'abcabc')
# out: ['abcabc']
# 可以看出,捕獲版本和非捕獲版本的區(qū)別
# 捕獲版本會捕獲到()分組內(nèi)的匹配字符
# 非捕獲版本會將()分組內(nèi)的字符與外面的字符作為一個整體返回
# 看一個嵌套捕獲的例子
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、(?#…)
介紹
注釋信息,里面的內(nèi)容會被忽略。
示例
re.findall('abc(?#這是注釋)123', 'abc123')
# out: ['abc123']
5、(?=…), (?!…)
介紹
-
(?=…):匹配…的內(nèi)容。這個叫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 ']
# 為了顯示區(qū)別,我們加了 '.' 匹配數(shù)字 1、2
# 從中可以看出,只有后面 不 是 'Asimov' 的時候才匹配 'Isaac '
看看,是不是一下子就明了了。
6、(?<=…), (?<?…)
介紹
- 匹配當(dāng)前位置之前是
...的樣式。這個叫positive lookbehind assertion(正向后視斷定) - 匹配當(dāng)前位置之前不是
...的樣式。這個叫negative lookbehind assertion(后視取反)
哈哈,這個又看不懂?
思考一下,既然有根據(jù)后面字符斷言的,那么根據(jù)前面字符來斷言,也是很合理的,
示例
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代表數(shù)字、字母和下?lián)Q線集合 - 第三個括號嵌套在第二個當(dāng)中,而且聲明非捕獲版本,是郵箱
.及后面的字符 - 最后一個括號當(dāng)中,
?(1)>|$:其中1 是對第一個括號分組的引用,如果存在,就匹配 >,否則匹配空
其結(jié)果匹配的就是<username@host.com>和username@host.com。
而不會匹配 <user@host.com' 和 <user@host.com
但是上面的第三個結(jié)果為啥不一樣呢?
因為findall允許返回空匹配的,在有 ?的情況下,所以它會分兩種情況去匹配
- 在
<存在的情況下,匹配不到>, - 在
<不存在時,能匹配到username@host.com
特殊序列
字符描述

簡單示例
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 和字符串開始/結(jié)尾的邊界
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']
總結(jié)
今天講了一些擴展標(biāo)記法,其實沒那么難,多看看例子,多練習(xí)練習(xí)。
下節(jié)將介紹 re 模塊各函數(shù)的用法,敬請期待......