目录
17-1 使用字符类编写匹配不同字符的模式
17-2 编写一个匹配字符串开头或结尾等位置的模式
17-3 编写一个模式,匹配一个重复前面的字符指定次数的字符串
17-4 编写匹配多个字符串中任意一个的模式
17-5 设置捕获组以获取匹配部分模式的字符串
17-6 使用 lookahead 和 lookbehind 编写模式
17-7 Pythonの正規表現で設定できるフラグの一覧
解释如何使用字符类,这是 Python 中可用的正则表达式元字符之一。一个字符类除了可以描述一个固定的字符串外,还可以描述一个匹配任意单个字符的模式,或者匹配多个候选字符中的任意一个的模式。
匹配指定字符
如果您在模式中写入一个字符,它会匹配该字符本身。例如,以下模式匹配任何以 D 开头,然后是 E,并以 F 结尾的字符串。
r'DEF'
匹配 DEF 、 ODEF 、 DEFE 等。与 DMEF 或 DE 不匹配。
〇 DEF
〇 DEFG
〇 CDEF
✕ DMEF
✕ DE
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'Sun')
print(bool(pattern.search('Today is Sunday')))
>> True
print(bool(pattern.search('The Sun rose')))
>> True
print(bool(pattern.search('Ham Sandwich')))
>> False
任何单个字符 (.)
元字符之一点 (.),匹配除换行符之外的任何单个字符。
.
例如,以下模式匹配任何以 G 开头,后跟任何单个字符,再后跟 P 的字符串。
r'G.P'
符合 GMP 和 GDP。与 GEO 或 GP 不匹配。
〇 GMP
〇 GDP
〇 AGOPB
✕ GEO
✕ GP
以下模式匹配任何以 G 开头后跟任意两个字母后跟 P 的字符串。
r'G..P'
匹配 GLMP 和 G89P。与 GOP 或 GP 不匹配。
〇 GLMP
〇 G89P
✕ GEP
✕ GP
我们举个简单的例子。
import re
pattern = re.compile(r'S..d')
print(bool(pattern.search('Today is Sunday')))
>> True
print(bool(pattern.search('The Sun rose')))
>> False
print(bool(pattern.search('Ham Sandwich')))
>> True
方括号中的单个字符 ([…])
方括号 ([…]) 是用于匹配方括号内的任何一个字符的元字符。
[...]
例如,以下模式匹配任何以 D 开头,后跟 E 、 F 或 G 中的任何一个,写在 [ 和 ] 之间,再后跟 H 的任何字符串。
r'D[EFG]H'
匹配 DFH 和 DGH。不匹配 DAH 或 DeH。
〇 DFH
〇 ADGHB
✕ DAH
✕ DeH
我们举个简单的例子。
import re
pattern = re.compile(r'[sS]un')
print(bool(pattern.search('Today is Sunday')))
>> True
print(bool(pattern.search('The sun rose')))
>> True
print(bool(pattern.search('Ham Sandwich')))
>> False
方括号的否定
如果在方括号([…])的开头写一个插入符(^)表示否定,则如果方括号内写的多个字符都不匹配,则匹配。
[^...]
例如,以下模式匹配以 D 开头的任何字符串,后跟不匹配 [to] 中的 E 、 F 或 G 中任何一个的单个字符,后跟 H 。
r'D[EFG]H'
请注意,它不仅否定紧跟在 ^ 之后的字符,而且否定写在 [ 和 ] 之间的所有字符。
匹配 DAH 和 D7H。与 DEH 或 DHH 不匹配。
〇 DAH
〇 anD7Hed
✕ DEH
✕ DHH
我们举个简单的例子。
import re
pattern = re.compile(r'199[^01234]')
print(bool(pattern.search('My birthday is 1996/04/06')))
>> True
print(bool(pattern.search('Graduated in 1994')))
>> False
print(bool(pattern.search('It opened in 1996')))
>> True
方括号范围规范
如果在方括号([…])的开头写一个插入符(^)表示否定,则如果方括号内写的多个字符都不匹配,则匹配。
[0-9] 从 0 到 9 的任何数字
[a-z] 从 a 到 z 的任何字母
[A-Z] 从 A 到 Z 的任何字母
例如,以下两个模式匹配相同的字符:
r'[A-G]'
r'[ABCDEFG]'
中括号[ ] 中可以写多个范围,并且可以与普通字符规范组合。以下模式匹配任何字符 a 到 e 、 g 、 m 、 x 到 z 。
r'[a-egmx-z]'
r'[abcdegmxyz]'
以下符号通常用作匹配任何字母数字字符的模式。
r'[0-9a-zA-Z]'
我们举个简单的例子。
import re
pattern = re.compile(r'199[0-6]')
print(bool(pattern.search('My birthday is 1998/03/12')))
>> False
print(bool(pattern.search('Graduated in 1994')))
>> True
print(bool(pattern.search('It opened in 1996')))
>> True
字符类速记
在字符类中,您可以通过描述 中括号[ ] 中的字符范围来描述与许多字符中的任何一个匹配的模式,但特别是经常使用的字符有一个简写符号它已经准备好了。Python 中可用的速记符号是:
\d Unicode 10 十进制数字([0-9] 和其他数字)
\D 非数字 [^\d]
\w Unicode 单词字符([a-zA-Z0-9_] 和大多数变成单词的字符)
\W 字母数字和非下划线 [^\w]
\s Unicode 空白([ \t\n\r\f\v] 和其他空白)
\S 非空白 [^\s]
\D 否定小写版本的大写版本,如 \d 否定。
但是,如果设置了 ASCII 标志,则范围会更加有限。
\d [0-9]
\D 非数字 [^\d]
\w [a-zA-Z0-9_]
\W 非字母数字和下划线 [^\w]
\s [ \t\n\r\ f \v]
\S 非空白字符 [^\s]
您可以使用 \d 代表任何数字,使用 \w 代表任何字母。
用一个简单的例子来说明一下。
import re
msg = 'zip code is 456-7890'
pattern = re.compile(r'\d{3}-\d{4}')
result = pattern.search(msg)
if result :
print(result.group(0))
else :
print('Don\'t matched')
>> 456-7890
换行符等特殊字符
提供了将反斜杠 (\) 与字符组合在一起的元字符,以编写匹配无法从键盘输入的特殊字符(例如换行符和制表符)的模式。
\f 换页
\n 改行
\r carriage return
\t tab
\v vertical tab
\b backspage (only in character classes)
\a terminal bell (BEL)
\xhh ASCII character with hexadecimal hh
\uxxxx 16-bit 16 Unicode character with十六进制值 xxxx
\Uxxxxxxxx 具有 32 位十六进制值的 Unicode 字符 xxxxxxxx
\N{name} Unicode 数据库中名称为 name 的字符
用一个简单的例子来尝试。
import re
msg = """您好。
今天的会议准时召开,
有什么变化请告知。"""
pattern = re.compile(r'。\n.')
result = pattern.search(msg)
if result :
print(result.group(0))
else :
print('Don\'t matched')
>> 。
>> 今
正则表达式提供匹配位置的元字符,例如字符串的开头或结尾、单词边界等,而不是字符本身。例如,如果您使用匹配字符开头的元字符,则它只会匹配从字符开头开始的后面的模式。在这里,我将解释如何使用 Python 正则表达式编写匹配字符串开头和结尾等位置的模式。
匹配字符串的开头 (^, \A)
元字符之一,插入符号 (^),匹配字符串的开头。
^
\A 是元字符之一,也匹配字符串的开头。
\A
例如,以下模式将匹配任何从字符串开头开始并以 R 开头,后跟 e 和 d 的字符串。
r'^Red'
匹配Red や Reduce 匹配,但是,如果不在字符串的开头,即使是Red也不会匹配。
〇 Red Color
〇 Reduce
✕ Radio
✕ Color is Red
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'^Red')
print(bool(pattern.search('Red Table')))
>> True
print(bool(pattern.search('Reduce speed')))
>> True
print(bool(pattern.search('Border Color is Red')))
>> False
使用 \A 而不是 ^ 具有相同的结果。
import re
pattern = re.compile(r'\ARed')
print(bool(pattern.search('Red Table')))
>> True
print(bool(pattern.search('Reduce speed')))
>> True
print(bool(pattern.search('Border Color is Red')))
>> False
匹配字符串的结尾 ($, \Z)
元字符之一,美元符号 ($),匹配字符串的末尾,如果有尾随换行符,则恰好在换行符之前。
$
\Z 是元字符之一,也匹配字符串的末尾。但是,如果末尾有换行符,则 \Z 不匹配。
\Z
例如,如果字符串 Script 出现在字符串的末尾,则以下模式将匹配。
r'Script$'
匹配 JavaScript 和 TypeScript。但是,如果不在字符串的末尾,即使是JavaScript也不会匹配。
〇 JavaScript
〇 I am studying TypeScript
✕ script
✕ PostScript is difficult
如果最后有一个换行符,它也会匹配换行符之前。
〇 JavaScript\n
〇 I am studying TypeScript\n
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'Script$')
print(bool(pattern.search('JavaScript')))
>> True
print(bool(pattern.search('I am studying TypeScript')))
>> True
print(bool(pattern.search('PostScript is difficult')))
>> False
print(bool(pattern.search('OpenScript\n')))
>> True
使用 \Z 而不是 $ 具有相同的结果,除了尾随换行符。如果有尾随换行符则不匹配。
import re
pattern = re.compile(r'Script\Z')
print(bool(pattern.search('JavaScript')))
>> True
print(bool(pattern.search('I am studying TypeScript')))
>> True
print(bool(pattern.search('PostScript is difficult')))
>> False
print(bool(pattern.search('OpenScript\n')))
>> False
匹配单词的开头和结尾 (\b)
一个元字符 \b 匹配单词的开头和结尾。
\b
例如,如果字符串 work 出现在单词的开头,则以下模式匹配。
r'\bwork'
匹配工作或工作。但它不匹配单词开头以外的任何内容,例如 fireworks。
〇 work
〇 a working person
✕ worm
✕ Go to see fireworks
\b 匹配单词的结尾和单词的开头。例如,如果字符串 ing 出现在单词的末尾,则以下模式匹配。
r'ing\b'
swimming ,working 匹配,但是,如果它不在单词的末尾,则不匹配,例如 ingenious。
〇 swimming
〇 a working person
✕ worm
✕ ingenious design
以下模式分别在单词的开头和结尾使用 \b,仅匹配单词 working。如果它前面或后面有字符,则它不匹配。
r'\bworking\b'
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'\bwork')
print(bool(pattern.search('a working person')))
>> True
print(bool(pattern.search('Go to see fireworks')))
>> False
pattern = re.compile(r'ing\b')
print(bool(pattern.search('a working person')))
>> True
print(bool(pattern.search('ingenious design')))
>> False
匹配单词开头和结尾以外的任何内容 (\B)
\B 是元字符之一,匹配单词开头或结尾以外的任何地方。
\B
例如,如果以下模式从单词开头以外的任何位置开始并且后跟 am,则它会匹配。
r'\Bam'
sample , program 匹配。但是,如果从amount这样的单词开头开始,则匹配不上。
〇 sample
〇 Difficult program
✕ full amount
例如,以下模式匹配字符串 am 之后不是单词结尾的任何位置。
r'am\B'
匹配样本和数量。但是,如果 am 在像 program 这样的单词的末尾,它就不会匹配。
〇 sample
〇 full amount
✕ Difficult program
示例代码
举一个简单的例子。
import re
pattern = re.compile(r'\Bam')
print(bool(pattern.search('Difficult program')))
>> True
print(bool(pattern.search('full amount')))
>> False
pattern = re.compile(r'am\B')
print(bool(pattern.search('full amount')))
>> True
print(bool(pattern.search('Difficult program')))
>> False
正则表达式还可以定义匹配字符串的模式,其中前面的字符重复指定次数。次数可以是1次以上,也可以是0次以上,也可以是重复次数。除了字符之外,还可以使用字符类等生成模式。本文介绍如何在 Python 正则表达式中编写模式,以匹配将字符或其他模式重复指定次数的字符串。
连续匹配前面的字符 0 次或多次 (*)
星号 (*) 是元字符之一,它与前面的字符连续匹配字符串 0 次或多次(0 次或多次意味着它不出现一次或多次都无关紧要)。
*
例如,以下模式匹配任何以 H 开头,后跟字母 a 零次或多次,再后跟 n 的字符串:
r'Ha*n'
匹配 Han 和 Haaaan。它还与 Hn 匹配,因为前面的字符出现 0 次或多次。
〇 Hn
〇 Han
〇 Haaan
〇 Haaaaaaaan
✕ Hen
✕ Ham
✕ HaaUaan
示例代码
举一个简单的例子来。
import re
pattern = re.compile(r'Ha*n')
print(bool(pattern.search('Hn')))
>> True
print(bool(pattern.search('Han')))
>> True
print(bool(pattern.search('Haaaaan')))
>> True
print(bool(pattern.search('HaaUaan')))
>> False
匹配前面的字符一次或多次 (+)
元字符之一,即加号 (+),与任何包含前面字符的字符串匹配一次或多次。
+
例如,以下模式匹配以 H 开头,后跟字母 a 一次或多次,再后跟 n 的任何字符串。
r'Ha+n'
匹配 Han 和 Haaaan。不匹配 Hn,因为前面的字符出现一次或多次。
✕ Hn
〇 Han
〇 Haaan
〇 Haaaaaaaan
✕ Hen
✕ Ham
✕ HaaUaan
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'Ha+n')
print(bool(pattern.search('Hn')))
>> False
print(bool(pattern.search('Han')))
>> True
print(bool(pattern.search('Haaaaan')))
>> True
print(bool(pattern.search('HaaUaan')))
>> False
匹配前面的字符 0 次或 1 次 (?)
元字符之一,问号 (?),与前面的字符匹配 0 次或 1 次。
?
例如,以下模式匹配任何以 H 开头的字符串,然后是字母零或一次,然后是 n:
r'Ha?n'
匹配 Hn 和 Han。它不匹配 Haaan,因为前面的字符出现 0 次或 1 次。
〇Hn
〇 Han
✕ Haaan
✕ Hen
✕ Ham
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'Ha?n')
print(bool(pattern.search('Hn')))
>> True
print(bool(pattern.search('Han')))
>> True
print(bool(pattern.search('Haaaaan')))
>> False
print(bool(pattern.search('Ham')))
>> False
匹配重复前一个字符 num 次的字符 ({num})
其中一个元字符 {num} 匹配由前面的字符重复 num 次组成的字符串。
{num}
例如,以下模式将匹配任何以 H 开头的字符串,后跟字母 a 三次,然后是 n :
r'Ha{3}n'
匹配Haaan。它不匹配 Han 或 Haaaaan,因为重复次数是固定的。
〇 Haaan
✕ Han
✕ Haaaaan
示例代码
举一个简单的例子来尝试。
import re
pattern = re.compile(r'Ha{3}n')
print(bool(pattern.search('Haaan')))
>> True
print(bool(pattern.search('Han')))
>> False
print(bool(pattern.search('Haaaaan')))
>> False
print(bool(pattern.search('HaaaO')))
>> False
匹配重复前一个字符至少 min 次且不超过 max 次的字符 ({min,max})
其中一个元字符 {min,max} 匹配一个字符串,该字符串重复前面的字符至少 min 次且不超过 max 次。您也可以只指定一个或另一个。
{min,} 重复前一个字符至少 min 次
{,max} 重复前一个字符最多 max 次
{min,max} 重复前一个字符最小到最大次数
请注意,如果您在最大值前放置一个半角空格,例如 {2, 4} 而不是 {2, 4},它将不会匹配。
例如,以下模式匹配任何以 H 开头、后跟重复 2 到 4 次的字母 a 和 n 的字符串。
r'Ha{2,4}n'
匹配 Haan 或 Haaan 或 Haaaan。它不匹配 Han 或 Haaaaan,因为重复次数是固定的。
〇 Haan
〇 Haaan
〇 Haaaan
✕ Han
✕ Haaaaan
示例代码
举一个简单的例子来尝试。
import re
pattern = re.compile(r'Ha{2,4}n')
print(bool(pattern.search('Haan')))
>> True
print(bool(pattern.search('Haaaan')))
>> True
print(bool(pattern.search('Haaaaan')))
>> False
print(bool(pattern.search('HaaaO')))
>> False
重复以前的模式
到目前为止所描述的重复元字符不仅匹配前面的字符,还匹配任何重复前面另一个模式的字符串。例如,如果您在表示任何字符的点 (.) 后面紧跟一个星号 (*),它将匹配任何重复任何字符 0 次或多次的字符串。
.*
以下模式匹配任何以 <p> 开头,后跟任何字符零次或多次,并以 </p> 结尾的字符串。
r'<p>.*</p>'
此外,如果在匹配 0 到 9 中任意字符的字符类中紧跟在 [0-9] 之后写入 {4},它将匹配任意 4 个连续数字的字符串。
[0-9]{4}
以下模式匹配以任何数字出现 3 次、后跟连字符 (-)、最后出现任何数字 4 次的任何字符串。
r'[0-9]{3}-[0-9]{4}'
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'<em>.*</em>')
msg = '<p>今天<em>晴天</em></p>';
result = pattern.search(msg)
print(result.group(0))
>> <em>晴天</em>
pattern = re.compile(r'[0-9]{3}-[0-9]{4}')
msg = 'zip code is 123-4567';
result = pattern.search(msg)
print(result.group(0))
>> 123-4567
重复分组模式
到目前为止,重复的目标是可以匹配紧接其前面的字符或模式。
例如,让我们创建一个重复 1 到 3 个数字后跟一个点 (.) 的字符串 3 次的模式。以一到三位数字开头后跟一个点 (.) 的字符串模式可以写成转义为 \.)。
r'\d{1,3}\.'
由于这是一个将整个模式重复三次的模式,而不是简单地添加 {3},因此在将整个目标模式括在括号 () 中后写入 {3} 以对其进行分组。
r'(\d{1,3}\.){3}'
这样就完成了由 1 到 3 个数字组成的字符串重复 3 次后跟一个点 (.) 的模式。
示例代码
举一个简单的例子来尝试。
import re
pattern = re.compile(r'(\d{1,3}\.){3}\d{1,3}')
msg = 'IP address is 192.168.0.18';
result = pattern.search(msg)
print(result.group(0))
192.168.0.18
最大量词和最小量词
默认情况下,像 + 和 * 这样的量词会尝试匹配更多的字符串。看看下面的示例。
import re
pattern = re.compile(r'b.*a')
msg = 'breakfast is sandwich';
result = pattern.search(msg)
print(result.group(0))
breakfast is sa
此模式匹配任何以 b 开头,后跟零次或多次出现的任何字符后跟 a 的字符串。在目标字符串中,a 出现在 b 之后的位置有 3 个。
breakfast is sandwich
breakfast is sandwich
breakfast is sandwich
正如您在前面的示例中所见,实际匹配项是最右侧的 a。这是因为默认情况下,像 +、* 和 {min,max} 这样的量词会尝试匹配更多的字符串。这样的量词称为最大量词。
在使用 + 和 * 等量词时,使用最少的量词来匹配较少的字符串。在每个量词后写一个问号(?)。
*?
+?
??
{min, max}?
让我们使用最小量词重写前面的例子。
import re
pattern = re.compile(r'b.*?a')
msg = 'breakfast is sandwich';
result = pattern.search(msg)
print(result.group(0))
brea
在这个例子中,它匹配了最少的可能匹配字符串。
正则表达式允许您描述一组替代字符串,并定义一个模式,如果其中任何一个匹配,该模式将匹配。本节介绍如何在 Python 正则表达式中编写匹配多个字符串之一的模式。
匹配多个字符串之一 (|)
例如,以下模式将匹配任何匹配 Sunday 或 Sun 或 sunday 的字符串。
文字列|文字列|...
例如,以下模式将匹配任何匹配 Sunday 或 Sun 或 sunday 的字符串。
r'Sunday|Sun|sunday'
匹配Sunday或Sun 或sunday 。它还匹配包含任何这些字符串的字符串,因为在它之前或之后没有其他模式。
〇 Go out on sunday
〇 Today is Sunday
〇 Sunflowers are in bloom
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'Sunday|Sun|sunday')
print(bool(pattern.search('Today is Sunday')))
>> True
print(bool(pattern.search('Sunflowers are in bloom')))
>> True
print(bool(pattern.search('SUNDAY MORNING')))
>> False
用括号将多项选择分组
如果您编写的模式与多个字符串替代项中的一个匹配,那么如果您将它与之前或之后的另一个模式组合,则不能按原样编写。比如你试着在red或者red的选择前后加上\b,它是一个词的边界,如果你这样写,就会是一个选择\bRed或者re\b。
r'\bRed|red\b'
因此,我们将选项部分括在括号()中,对它们进行分组。通过分组,您可以明确选项部分的开始和结束位置。
r'\b(Red|red)\b'
此模式匹配 Red 或 red 作为单个词。
〇 Red Rose
✕ reduce the workload
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'\b(Red|red)\b')
print(bool(pattern.search('Red Rose')))
>> True
print(bool(pattern.search('reduce the workload')))
>> False
print(bool(pattern.search('Border color is red')))
>> True
print(bool(pattern.search('predict a result')))
>> False
通过在正则表达式模式中设置捕获组,您可以将匹配部分模式的字符串与匹配整个模式的字符串分开。本节介绍如何在 Python 正则表达式中使用捕获组。
设置捕获组并获取捕获(|)
要在正则表达式模式中设置捕获组,请将要设置的部分括在括号 () 中。
r'abc(def)ghi'
这里使用的括号与分组时指定的括号相同。用于分组的部分模式周围的括号也设置了捕获组。
例如,请参见下面的模式。
r'\d{3}-\d{4}'
字符串 ‘zip code is 123-4567’ 匹配这个模式,匹配整个模式的字符串是 123-4567。这时设置一个捕获组,只获取模式中匹配\d{4}的部分。要设置捕获组,请将括号放在要设置的模式部分周围。
r'\d{3}-(\d{4})'
现在,当字符串匹配模式时,我们得到匹配捕获组部分的 4567,除了匹配整个模式的字符串 123-4567。匹配捕获组的字符串称为捕获。
可以在一个模式中设置多个捕获组。
r'(\d{3})-(\d{4})'
如果设置了多个捕获组,则从前面依次设置捕获组1、捕获组2等,然后通过指定捕获组的索引来引用捕获的字符串。
示例代码
举一个简单的例子来尝试。
import re
pattern = re.compile(r'(\d{3})-(\d{4})')
msg = 'zip code is 123-4567。';
result = pattern.search(msg)
print(result.group(0))
>> 123-4567
print(result.group(1))
>> 123
print(result.group(2))
>> 4567
即使分组括号在模式中括在括号 () 中时也会设置一个捕获组。如果不打算使用capture,也不想显式capture,可以设置一个不capture的capture group,写成下面的,而不是括号()。
(?:模式)
不要用“(”和“)”括起来,而是用“(?:”和“)”括起来。以这种格式包围与分组具有相同的功能,但不执行捕获。用于不打算捕获的组很方便,比如当你想通过迭代处理按顺序获取捕获组的值时。
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'产品(?:Code|代码):([A-Z]{2})-(\d{2})')
msg = '产品代码:AZ-07';
result = pattern.search(msg)
print(result.group(1))
>> AZ
print(result.group(2))
>> 07
本例中设置了三个捕获组,但是由于第一个捕获组设置了不捕获,所以按顺序捕获时,第二个捕获组和第三个捕获组都被捕获了。
使用命名捕获组
使用named capturing groups,你可以为每个捕获组设置一个名称,并通过名称而不是通过索引来引用捕获的值。格式如下。
(?P<group name> 模式)
在普通的捕获组中,我们通常通过在匹配对象的组方法的参数中指定捕获组的索引来引用捕获组捕获的值。对于命名捕获组,您可以通过将捕获组的名称指定为组方法的参数来引用捕获的值。
match object.group('groupname')
示例代码
用一个简单的例子来尝试。
import re
pattern = re.compile(r'产品代码:(?P<section>[A-Z]{2})-(?P<code>\d{2})')
msg = '产品代码:JP-82';
result = pattern.search(msg)
print(result.group('section'))
>> JP
print(result.group('code'))
>> 82
与捕获值匹配相同值的模式
如果在模式中设置捕获组,则可以从同一模式中引用捕获组匹配的值,并编写模式以匹配与已捕获值相同的值。
引用时,在pattern中写\1引用capture 1的值,\2引用capture 2的值,以此类推。
\capture号码
以下模式稍后引用第一个捕获组捕获的值。
r'(ABC).*\1'
在这种情况下,模式以 ABC 开头,后跟任何字符零次或多次,然后是与第一个捕获组中捕获的值相同的值。第一个捕获组捕获的值是ABC,所以它和模式r’ABC.*ABC’是一样的。
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'<(.+)>.*</\1>')
msg = 'AAA <div>BBB<span>CCC</span>DDD</div>EE';
result = pattern.search(msg)
print(result.group(0))
>> <div>BBB<span>CCC</span>DDD</div>
在此示例中,我们匹配包含在结束标记(在本例中为 </div>)的字符串,其名称与找到的第一个标记(在本例中为 <div>)相同。我们稍后会引用捕获的值来查找具有相同名称的结束标记。
检查匹配时使用正则表达式中的先行和后行,但不将其作为匹配字符串检索。Lookahead有positive和negative lookahead,lookbehind有positive和negative lookahead。本文将向您展示如何使用 Python 在正则表达式中使用 lookahead 和 lookbehind。
什么是正则表达式中的先行和后行
lookahead(positive lookbehind,negative lookbehind)和lookbehind(positive lookbehind,negative lookbehind)格式如下:
Pattern A (?= Pattern B) 正面前瞻
Pattern A (?! Pattern B) 否定前瞻
(?<=pattern B) pattern A 正向后视
(?<!Pattern B) Pattern A 负向后视
Lookbehind 匹配模式 A 紧跟在模式 B 之后(或不匹配),而 lookbehind 匹配模式 A 紧接在模式 B 之前(或不匹配)。两者都判断是否存在包含模式 B 的匹配,但只获取与模式 A 匹配的部分作为匹配字符串。
让我们按顺序来看。
肯定先行
如果主题字符串在模式 B 之后紧跟模式 A,则正先行匹配。此时,仅取得与模式A一致的部分作为一致值。
Pattern A (?= Pattern B) 正面前瞻
例如,考虑以下模式。
r'smart(?=phone)'
仅匹配智能后跟电话。匹配智能手机,但不匹配智能或智能手表。只有聪明的人才能将其作为匹配的字符串获取。
示例代码
用一个简单的例子来尝试。
import re
pattern = re.compile(r'smart(?=phone)')
print(bool(pattern.search('smart')))
>> False
print(bool(pattern.search('smartphone')))
>> True
print(bool(pattern.search('smartwatch')))
>> False
result = pattern.search('smartphone')
print(result.group(0))
>> smart
否定前瞻
如果目标字符串没有紧跟模式 B 的模式 A,则负先行匹配。此时,仅取得与模式A一致的部分作为一致值。
Pattern A (?! Pattern B) 否定前瞻
例如,考虑以下模式。
r'smart(?!phone)'
仅当 smart 后跟的不是 phone 时才匹配。不匹配智能手机,但匹配智能和智能手表。只有聪明的人才能将其作为匹配的字符串获取。
示例代码
用一个简单的例子来尝试。
import re
pattern = re.compile(r'smart(?!phone)')
print(bool(pattern.search('smart')))
>> True
print(bool(pattern.search('smartphone')))
>> False
print(bool(pattern.search('smartwatch')))
>> True
result = pattern.search('smartwatch')
print(result.group(0))
>> smart
肯定的回头看
如果主题字符串在模式 A 之前具有模式 B,则正向后视匹配。此时,仅取得与模式A一致的部分作为一致值。
(?<=pattern B) pattern A 正向后视
例如,考虑以下模式。
r'(?<=digital)camera'
仅当camera紧跟在digital之前时才匹配。它匹配 digitalcamera,但不匹配 camera 或 analogcamera。而且您只会将camera作为匹配的字符串。
示例代码
用一个简单的例子来尝试。
import re
pattern = re.compile(r'(?<=digital)camera')
print(bool(pattern.search('camera')))
>> False
print(bool(pattern.search('digitalcamera')))
>> True
print(bool(pattern.search('analogcamera')))
>> False
result = pattern.search('digitalcamera')
print(result.group(0))
>> camera
负面回顾
如果主题字符串中的模式 B 不在模式 A 之前,则负向后查找匹配。此时,仅取得与模式A一致的部分作为一致值。
(?<!Pattern B) Pattern A 负向后视
例如,考虑以下模式。
r'(?<!digital)camera'
仅当 camera 前面没有紧跟 digital 时才匹配。不匹配 digitalcamera,但匹配 camera 和 analogcamera。而且您只会将camera作为匹配的字符串。
示例代码
我会用一个简单的例子来尝试。
import re
pattern = re.compile(r'(?<!digital)camera')
print(bool(pattern.search('camera')))
>> True
print(bool(pattern.search('digitalcamera')))
>> False
print(bool(pattern.search('analogcamera')))
>> True
result = pattern.search('analogcamera')
print(result.group(0))
>> camera
正则表达式允许您设置标志(或选项)来更改字符串与模式的匹配方式。在这里,我们将解释可以在 Python 中设置的标志类型以及如何使用每个标志。
如何在 Python 中设置标志
在 Python 中可以通过三种方式来标记正则表达式模式。第一种是在编译模式时将标志设置为第二个参数,第二种是在编写模式时将标志放在模式的开头,第三种是在编写模式时编写仅适用的标志的方法一些模式。
编译模式时指定标志
这是第一种方法。为了编译模式并创建 Pattern 类的实例,我们使用了 re 模块中的编译函数,其格式如下:
re.compile(pat, flags=0)
在可选的第二个参数中指定标志。例如,要指定 re.ASCII 标志,编写如下。
pattern = re.compile(r'A.+B', re.ASCII)
要设置多个标志,请使用竖线 (|) 指定多个标志。
pattern = re.compile(r'A.+B', re.ASCII|re.MULTILINE)
此方法将标志应用于整个模式。
在模式的开头写标志
二つ目の方法です。パターンを記述するときにパターンの先頭にフラグを記述します。第二种方法。写pattern的时候把flag写在pattern的开头。
r'(?标志)模式'
在 flags 的地方,指定代表 ai L msux 的 7 种标志类型的字母。如果要指定多个,请连续写入。
r'(?a)A.+B' // re.ASCII
r'(?am)A.+B' // re.ASCII + re.MULTILINE
此方法将标志应用于整个模式。
描述应用于部分模式的标志
第三种方法。编写模式时,编写仅适用于部分模式的标志。(这是 Python 3.6 中添加的功能)。
r'模式(?标志:模式)'
如果要设置标志,请指定代表 ai L msux 的 7 个标志之一的字符。如果只想取消应用于整体的这部分标志,请在-之后指定代表imsx的四种标志的字符串。
以下将 re.ASCII 设置为仅部分模式。
r'[a-z]{4}-(?a:[a-z]{4})'
下面,当为整个模式设置 re.ASCII 时,部分清除 re.ASCII。
r'(?a)[a-z]{4}-(?-a:[a-z]{4})'
下面,当为整个模式设置 re.ASCII 时,部分清除 re.ASCII 并设置 re.MULTILINE。
r'(?a)[a-z]{4}-(?m-a:[a-z]{4})'
使用此方法,您可以仅标记或取消标记部分模式。
Python 正则表达式中可用的标志类型
下面是Python提供的flags的类型和解释。提供了七种类型的标志。
标志 | 别名 | in line | 解释 |
re.ASCII | re.A | (?a) | 元字符 \w 、 \b 、 \d 和 \s 将只匹配 ASCII 字符。 |
re.DEBUG | — | — | 打印有关模式的调试信息。 |
re.IGNORECASE | re.I | (?i) | 使模式不区分大小写。 |
re.LOCALE | re.L | (?L) | 使元字符 \w 、 \b 和不区分大小写的匹配依赖于当前语言环境。 |
re.MULTILINE | re.M | (?m) | 多线模式。 元字符 ^ 和 $ 现在不仅匹配字符串的开头和结尾,还匹配每行的开头和结尾。 |
re.DOTALL | re.S | (?s) | 元字符 . 现在也匹配换行符。 |
re.VERBOSE | re.X | (?x) | 评论现在可以写成模式。 |
现在让我们仔细看看 re.DEBUG 和 re.LOCALE 以外的标志。
re.ASCII标志
设置 re.ASCII 标志会导致元字符 \w 、 \W 、 \b 、 \B 、 \d 、 \D 、 \s 和 \S 仅匹配 ASCII 字符。
也可以使用 re.A 而不是 re.ASCII。另外,直接在模式中写入时使用 (?a) 。
※ re.A 也可以用来代替 re.ASCII。另外,直接在模式中写入时使用 (?a) 。
默认情况下,它匹配以下字符:
\d Unicode 十进制数字([0-9] 和其他数字)
\D 非数字 [^\d]
\w Unicode 单词字符([a-zA-Z0-9_] 和大多数变成单词的字符)
\W 非- 字母数字和非下划线 [^\w]
\s Unicode 空白([ \t\n\r\f\v] 和其他空白)
\S 非空白 [^\s]
设置 re.ASCII 标志将只匹配 ASCII 字符。
\d [0-9]
\D 非数字 [^\d]
\w [a-zA-Z0-9_]
\W 非字母数字和下划线 [^\w]
\s [ \t\n\r\ f \v]
\S 非空白字符 [^\s]
用一个简单的例子来尝试。
import re
pattern = re.compile(r'\w{4}')
print(bool(pattern.search('ABCD')))
>> True
print(bool(pattern.search('青岛市区')))
>> True
pattern = re.compile(r'\w{4}', re.ASCII)
print(bool(pattern.search('ABCD')))
>> True
print(bool(pattern.search('青岛市区')))
>> False
\w 现在通过设置 re.ASCII 标志被视为仅 ASCII [a-zA-Z0-9_]。
re.IGNORECASE 标志
设置 re.IGNORECASE 标志以忽略您在模式中写入的字母表的大小写。
可以使用 re.I 而不是 re.IGNORECASE。另外,直接在模式中写入时使用 (?i)。
例如,以下三种模式匹配相同的字符串:
pattern = re.compile(r'[a-zA-Z]')
pattern = re.compile(r'[a-z]', re.IGNORECASE)
pattern = re.compile(r'[A-Z]', re.IGNORECASE)
用一个简单的例子来尝试。
import re
pattern = re.compile(r'flower')
print(bool(pattern.search('flower')))
>> True
print(bool(pattern.search('Flower')))
>> False
print(bool(pattern.search('FLOWER')))
>> False
pattern = re.compile(r'flower', re.IGNORECASE)
print(bool(pattern.search('flower')))
>> True
print(bool(pattern.search('Flower')))
>> True
print(bool(pattern.search('FLOWER')))
>> True
通过设置 re.IGNORECASE 标志,模式现在不区分大小写。
re.MULTILINE 标志
如果设置 re.MULTILINE 标志,元字符 ^ 和 $ 将匹配行的开头和结尾以及字符串的开头和结尾。
可以使用 re.M 而不是 re.MULTILINE。另外,直接在模式中写入时使用 (?m)。
用一个简单的例子来尝试。
import re
msg = """Today we're studying JavaScript.
Tomorrow will study the TypeScript."""
pattern = re.compile(r'\w{4}Script\.$')
result = pattern.findall(msg)
print(result)
>> ['TypeScript.']
pattern = re.compile(r'\w{4}Script\.$', re.MULTILINE)
result = pattern.findall(msg)
print(result)
>> ['JavaScript.', 'TypeScript.']
设置 re.MULTILINE 标志现在使 $ 匹配行的结尾以及字符串的结尾。
re.DOTALL
如果设置 re.DOTALL 标志,元字符 . 也将匹配换行符。
可以使用 re.S 代替 re.DOTALL。另外,直接在模式中写入时使用 (?s)。
用一个简单的例子来尝试。
import re
msg = """我住在山东省青岛市市南区。"""
pattern = re.compile(r'<span>.*</span>')
print(bool(pattern.search(msg)))
>> False
pattern = re.compile(r'<span>.*</span>', re.DOTALL)
print(bool(pattern.search(msg)))
>> True
现在还通过设置 re.DOTALL 标志来匹配换行符。
re.VERBOSE
可以通过设置 re.VERBOSE 标志将注释放入模式中
也可以使用 re.X 而不是 re.VERBOSE。另外,直接在模式中写入时使用 (?x)。
具体来说,如果在模式行中描述了除 \# 之外的 #,则从 # 到行尾的行将被视为注释并被忽略。除了在字符类内部和反斜杠之后,空格也会被忽略。
例如,以下模式。
pattern = re.compile(r'[a-zA-Z]{2}-[0-9]{3}')
设置 re.VERBOSE 标志允许您为以下准备好的模式编写注释。
pattern = re.compile(r"""[a-zA-Z]{2} # 产品名称
- # 连字符
[0-9]{3} # 产品编号""", re.VERBOSE)
通过以这种方式划分模式并编写注释,即使在复杂的模式中也可以弄清楚哪个部分意味着什么。
用一个简单的例子来尝试。
import re
pattern = re.compile(r"""[a-zA-Z]{2} # 产品名称
- # 连字符
[0-9]{3} # 产品编号""", re.VERBOSE)
print(bool(pattern.search('AZ-243')))
即使写了评论,也能够确认该模式可以正常工作。