一、正则表达式简介
正则表达式(regex):
由一些字符和特殊符号组成的字符串,描述了模式的重复或者表述多个字符,可以按照某种模式匹配一系列有相似特征的字符串。为高级的文本模式匹配、抽取、与/或文本形式的搜索和替换功能提供了基础。
也就是是说,使用正则表达式能够匹配多个既定规则的字符串。在严格表述与字符串中模式相关的正则表达式时,一般使用术语“匹配”(matching),即专业术语“模式匹配”(pattern-matching)。
Python中的正则:
Python 通过标准库中的 re 模块来支持正则表达式。
Python 术语中,主要有两种方法完成模式匹配:
- 搜索(searching):在字符串任意部分中搜索匹配的模式;通过 search()函数或方法来实现
- 匹配(matching):指判断一个字符串能否从起始处全部或者部分地匹配某个模式;通过调用 match()函数或方法实现。
二、正则表达式
正则表达式的强大之处在于引入特殊字符来定义字符集、匹配子组和重复模式。正是由于这些特殊符号,给予了正则表达式强大的
功能和灵活性使,得正则表达式可以匹配字符串集合,而不仅仅只是某单个字符串。
2.1、正则表达式中的特殊符号:
特殊符号 | 描述 | 正则表达式示例 |
---|---|---|
literal | 匹配文本字符串的字面值 literal | aaa |
. | 匹配任何字符(除了\n 之外) | aaa |
re1|re2 | 匹配正则表达式 re1 或者 re2 | $2 |
^ | 匹配字符串起始部分 | ^aa |
$ | 匹配字符串终止部分 | bin/*sh$ |
* | 匹配 0 次或者多次前面出现的正则表达式 | [A-Za-z0-9]* |
+ | 匹配 1 次或者多次前面出现的正则表达式 | [a-z]+.com |
? | 匹配 0 次或者 1 次前面出现的正则表达式 | go? |
{N} | 匹配 N 次前面出现的正则表达式 | [0-9]{3} |
{M,N} | 匹配 M~N 次前面出现的正则表达式 | [0-9]{5,9} |
[…] | 匹配来自字符集的任意单一字符 | [aeiou] |
[x-y] | 匹配 x~y 范围中的任意单一字符 | [0-9], [A-Za-z] |
[^…] | 不匹配此字符集中出现的任何一个字符,包括某一范围的字符(如果在此字符集中出现) | [^A-Za-z0-9] ,[^aeiou] |
(*|+|?|{})? | 用于匹配上面频繁出现/重复出现符号的非贪婪版本(*、+、?、{}) | .*?[a-z] |
(…) | 匹配封闭的正则表达式,然后另存为子组 | ([0-9]{3})?,f(oo |
择一匹配符号:
即管道符号(|),也就是键盘上的竖线,表示一个“从多个模式中选择其一”的操作,用于分割不同的正则表达式。
正则表达式 | 匹配的字符串 |
---|---|
aa|bb | aa或bb |
aa|bb|cc | aa或bb或cc |
匹配任意单个字符:
即英文格式句点(.)符号,匹配除了换行符\n 以外的任何一个字符,无论字母、数字、空格(并不包括“\n”换行符)、可打印字符、不可打印字符。
Python中编译标记[S 或者 DOTALL],该标记能够推翻该限制,从而能够匹配换行符。
正则表达式 | 匹配的字符串 |
---|---|
a.z | 匹配在字母“a”和“z”之间的任意一个字符;例如 abz、a6z、a#z 等 |
.. | 任意两个字符 |
.end | 匹配在字符串 end 之前的任意一个字符 |
\. | 匹配英文格式句点 |
字符串起始、结尾、边界:
字符串的起始匹配符:
即脱字符(^)或者特殊字符\A(反斜线和大写字母 A)
字符串的结尾匹配符:
即美元符号($)或者\Z
正则表达式 | 匹配的字符串 |
---|---|
^from | 任何以 From 作为起始的字符串 |
enn$ | 任何以enn作为结尾的字符串 |
^ff:ee$ | 任何由单独的字符串 ff:ee 构成的字符串 |
\$$ | 任何以美元符号($)结尾的字符串 |
THE | 任何包含 THE 的字符串 |
\bTHE | 任何以 THE 开始的字符串 |
\bTHE\b | 仅仅匹配单词 THE |
\BTHE | 任何包含但并不以 THE 作为起始的字符串 |
字符集匹配符:
即方括号[],可以匹配特定字符,还支持匹配指定的字符范围。
方括号中两个符号中间用连字符(-)连接,用于指定一个字符的范围,例如,A-Z、a-z 或者 0-9 分别用于表示大写字母、小写字母和数值数字。
正则表达式 | 匹配的字符串 |
---|---|
a[admin]z | aaz、adz、amz、aiz、anz中任意一个 |
[ab][34][cd][56] | 一个包含四个字符的字符串,第一个字符是“a”或“b”,然后是“3”或“4”,后面是“c”或“d”,最后要么是“5”要么是“6”。例如,a3c5、b4d6 等 |
z.[0-9] | 字母“z”后面跟着任何一个字符,然后跟着一个数字 |
[a-d][efgh-i][A-Z] | 第一个为a、b、c、d中的任意一个,第二个为e、f、g、h、i中的任意一个,第三个为A到Z之间的任意一个 |
否定字符:
如果脱字符(^)紧跟在左方括号后面,这个符号就表示不匹配给定字符集中的任何一个字符。
正则表达式 | 匹配的字符串 |
---|---|
[^aeiou] | 一个非元音字符 |
[^\t\n] | 不匹配制表符或者\n |
[“-a] | 在一个 ASCII 系统中,所有字符都位于“”和“a”之间,即 34~97 之间 |
闭包操作符:
最常用的正则表达式符号一般为星号操作符()、加号操作符(+)、重载操作符(?)。
星号操作符(),匹配其左侧出现0次或多次,在计算机编程语言和编译原理中,该操作称为 Kleene 闭包。
加号(+)操作符,匹配一次或者多次(也叫做正闭包操作符)。
正则表达式 | 匹配的字符串 |
---|---|
[dn]ot? | 字母“d”或者“n”,后面跟着一个“o”,然后是最多一个“t”,例如,do、no、dot、not |
0?[1-9] | 任何数值数字,它可能前置一个“0”,例如,匹配一系列数(表示从 1~9 的数值),不管是一个还是两个数字 |
[0-9]{15,16} | 匹配 15 或者 16 个数字(例如信用卡号码) |
</?[^>]+> | 匹配全部有效的(和无效的)HTML 标签 |
[KQRBNP][a-h][1-8]-[a-h][1-8] | 在“长代数”标记法中,表示国际象棋合法的棋盘移动(仅移动,不包括吃子和将军)。即“K”、“Q”、“R”、“B”、“N”或“P”等字母后面加上“a1”~“h8”之间的棋盘坐标。前面的坐标表示从哪里开始走棋,后面的坐标代表走到哪个位置(棋格)上 |
2.2、正则表达式中的特殊字符:
特殊字符 | 描述 | 正则表达式示例 |
---|---|---|
\d | 匹配任何十进制数字,与[0-9]一致(\D 与\d 相反,不匹配任何非数值型的数字) | data\d+.txt |
\w | 匹配任何字母数字字符,与[A-Za-z0-9_]相同(\W 与之相反) | [A-Za-z_]\w+ |
\s | 匹配任何空格字符,与[\n\t\r\v\f]相同(\S 与之相反) | of\sthe |
\b | 匹配任何单词边界(\B 与之相反) | \bThe\b |
\N | 匹配已保存的子组 N(参见上面的(…)) | price: \16 |
\c | 逐字匹配任何特殊字符 c(即,仅按照字面意义匹配,不匹配特殊含义) | ., \, * ., \, * |
\A(\Z) | 匹配字符串的起始(结束)(另见上面介绍的^和$) | \ADear |
\f | 匹配一个换页符,等价于 \x0c 和 \cL | |
\n | 匹配一个换行符,等价于 \x0a 和 \cJ | |
\r | 匹配一个回车符,等价于 \x0d 和 \cM | |
\t | 匹配一个制表符,等价于 \x09 和 \cI | |
\v | 匹配一个垂直制表符,等价于 \x0b 和 \cK |
特殊字符表示字符集:
特殊字符\d 表示匹配任何十进制数字,\w表示全部字母数字的字符集(相当于[A-Za-z0-9_]),\s 可以用来表示空格字符,这些特殊字符的
大写版本表示不匹配。特殊字符组合使用,可以表示更复杂的示例。
正则表达式 | 匹配的字符串 |
---|---|
\w±\d+ | 一个由字母数字组成的字符串和一串由一个连字符分隔的数字 |
[A-Za-z]\w* | 第一个字符是字母;其余字符(如果存在)可以是字母或者数字(几乎等价于 Python 中的有效标识符) |
\d{3}-\d{3}-\d{4} | 美国电话号码的格式,前面是区号前缀,例如 800-555-1212 |
\w+@\w+.com | 以 [email protected] 格式表示的简单电子邮件地址 |
2.3、正则表达式中的扩展表示法:
扩展表示法 | 描述 | 正则表达式示例 |
---|---|---|
(?iLmsux) | 在正则表达式中嵌入一个或者多个特殊“标记”参数(或者通过函数/方法) | (?x),(? im) |
(?..) | 表示一个匹配不用保存的分组 | (?:\w+.)* |
(?P…) | 像一个仅由 name 标识而不是数字 ID 标识的正则分组匹配 | (?P) |
(?P=name) | 在同一字符串中匹配由(?P<name)分组的之前文本 | (?P=data) |
(?#…) | 表示注释,所有内容都被忽略 | (?#comment) |
(?=…) | 匹配条件是如果…出现在之后的位置,而不使用输入字符串;称作正向前视断言 | (?=.com) |
(?!…) | 匹配条件是如果…不出现在之后的位置,而不使用输入字符串;称作负向前视断言 | (?!.net) |
(?<=…) | 匹配条件是如果…出现在之前的位置,而不使用输入字符串;称作正向后视断言 | (?<=800-) |
(?<!…) | 匹配条件是如果…不出现在之前的位置,而不使用输入字符串;称作负向后视断言 | (?<!192.168.) |
(?(id/name)Y|N ) | 如果分组所提供的 id 或者 name(名称)存在,就返回正则表达式的条件匹配 Y,如果不存在,就返回 N;|N 是可选项 | (?(1)y|x) |
圆括号指定分组:
对于之前匹配成功的数据,在确定整个字符串是否匹配标准后,若想提取任何已经成功匹配的特定字符串或者子字符串,则需要引入圆括号来进行分组,这样匹配模式的子字符串可以保存起来供后续使用,这些子组能够被同一次的匹配或者搜索重复调用,或者提取出来用于后续处理。
使用正则表达式时,一对圆括号可以实现以下任意一个(或者两个)功能:
- 对正则表达式进行分组;
- 匹配子组。
应用场景:
- 两个不同的正则表达式,想用它们来比较同一个字符串;
- 对正则表达式进行分组可以在整个正则表达式中使用重复操作符。
正则表达式 | 匹配的字符串 |
---|---|
\d+(.\d*)? | 表示简单浮点数的字符串;也就是说,任何十进制数字,后面可以接一个小数点和零个或者多个十进制数字,例如“0.004”、“2”、“75.”等 |
(Mr?s?.)?[A-Z][a-z]*[A-Za-z-]+ | 名字和姓氏,以及对名字的限制(如果有,首字母必须大写,后续字母小写),全名前可以有可选的“Mr.”、“Mrs.”、“Ms.”或者“M.”作为称谓,以及灵活可选的姓氏,可以有多个单词、横线以及大写字母 |
扩展表示法:
以问号开始(?…),通常用于在判断匹配之前提供标记,实现一个前视(或者后视)匹配,或者条件检查。尽管圆括号使用这些符号,但是只有(?P)表述一个分组匹配,所有其他的都没有创建一个分组。
正则表达式 | 匹配的字符串 |
---|---|
(?:\w+.)* | 以句点作为结尾的字符串,例如“google.”、“twitter.”、“facebook.”,但是这些匹配不会保存下来供后续的使用和数据检索 |
(?#comment) | 此处并不做匹配,只是作为注释 |
(?=.com) | 如果一个字符串后面跟着“.com”才做匹配操作 |
(?!.net) | 如果一个字符串后面不是跟着“.net”才做匹配操作 |
(?<=800-) | 如果字符串之前为“800-”才做匹配,比如:电话号码 |
(?<!192.168.) | 如果一个字符串之前不是“192.168.”才做匹配操作,比如:过滤掉一组 C 类 IP 地址 |
(?(1)y|x) | 如果一个匹配组 1(\1)存在,就与 y 匹配;否则,就与 x 匹配 |
三、Python中的正则表达式之re 模块
Python 中通过使用 re 模块来支持正则表达式。
3.1、核心函数match、search
match(pattern,string,flags=0):
匹配字符串,带有可选标记的正则表达式的模式。匹配成功,返回匹配对象;匹配失败,返回 None。
只要模式从字符串的起始部分开始匹配,即使字符串比模式长,匹配也仍然能够成功
import re
r1 = re.match('test', 'test')
if r1 is not None:
g = r1.group()
print(g)
# 输出结果:test
r2 = re.match('test', 'test , The Second test demo')
if r2 is not None:
g = r2.group()
print(g)
# 输出结果:test
r3 = re.match('test', 'The test demo').group()
print(r3)
# 输出结果:AttributeError: 'NoneType' object has no attribute 'group'
search(pattern,string,flags=0):
搜索,带有可选标记的搜索字符串中第一次出现的正则表达式模式。匹配成功,返回匹配对象;匹配失败,返回 None。
在任意位置对给定正则表达式模式搜索第一次出现的匹配情况,概率远大于出现在字符串起始部分的概率。
import re
r1 = re.search('test', 'test')
if r1 is not None:
g = r1.group()
print(g)
# 输出结果:test
r2 = re.search('test', 'test , The Second test demo')
if r2 is not None:
g = r2.group()
print(g)
# 输出结果:test
r3 = re.search('test', 'The test demo').group()
print(r3)
# 输出结果:test
3.2、group()、 groups()
group(num=0):
匹配对象的 group()方法能够用于显示那个成功的匹配。返回整个匹配对象,或者编号为 num 的特定子组。
如果匹配失败,将会抛出 AttributeError 异常(None 是返回的错误值,该值并没有 group()属性[方法]),解决该异常添加if语句即可。
groups(default=None):
返回一个包含所有匹配子组的元组(如果没有成功匹配,则返回一个空元组)。
groupdict(default=None):
返回一个包含所有匹配的命名子组的字典,所有的子组名称作为字典的键(如果没有
成功匹配,则返回一个空字典)
3.3、查找每一次出现的位置findall()、finditer()
findall(pattern,string [, flags] ):
查找字符串中所有(非重复)出现的正则表达式模式,返回一个匹配列表。
r1 = re.findall('car', 'car')
print(r1)
# 输出结果:['car']
r2 = re.findall('car', 'scary')
print(r2)
# 输出结果:['car']
r3 = re.findall('car', 'carry the barcardi to the car')
print(r3)
# 输出结果:['car', 'car', 'car']
finditer(pattern,string [, flags] ):
与 findall()函数相同,但返回的不是一个列表,而是一个迭代器。对于每一次匹配,迭代器都返回一个匹配对象。
import re
s = 'This and that.'
r1 = re.findall(r'(th\w+) and (th\w+)', s, re.I)
print(r1)
# 输出结果:[('This', 'that')]
r2 = re.finditer(r'(th\w+) and (th\w+)', s, re.I)
print(type(r2))
# 输出结果:<class 'callable_iterator'>
for i in r2:
print(i.groups()) # 输出结果:('This', 'that')
print(i.group()) # 输出结果:This and that
print(i.group(1)) # 输出结果:This
print(i.group(2)) # 输出结果:that
r5 = [g.groups() for g in re.finditer(r'(th\w+) and (th\w+)', s, re.I)]
# 输出结果:print(r5)
[('This', 'that')]
3.4、compile()函数编译正则表达式
在模式匹配发生之前,正则表达式模式必须编译成正则表达式对象。由于正则表达式在执行过程中将进行多次比较操作,因此强烈建议使
用预编译。既然正则表达式的编译是必需的,那么使用预编译来提升执行性能无疑是明智之举。re.compile()能够提供此功能。
模块函数会对已编译的对象进行缓存,所以不是所有使用相同正则表达式模式的 search()和 match()都需要编译。
purge():清除隐式编译的正则表达式模式
**compile(pattern,flags = 0) **:
使用任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象。
推荐预编译,但并不是必需的。如果需要编译,就使用编译过的方法;如果不需要编译,就使用函数。方法和函数名字相同
常用的模块属性(用于大多数正则表达式函数的标记):
属性 | 描述 |
---|---|
re.I、re.IGNORECASE | 不区分大小写的匹配 |
re.L、re.LOCALE | 根据所使用的本地语言环境通过\w、\W、\b、\B、\s、\S 实现匹配 |
re.M、re.MULTILINE | ^和$分别匹配目标字符串中行的起始和结尾,而不是严格匹配整个字符串本身的起始和结尾 |
re.S、rer.DOTALL | “.”(点号)通常匹配除了\n(换行符)之外的所有单个字符;该标记表示“.”(点号)能够匹配全部字符 |
re.X、re.VERBOSE | 通过反斜线转义,否则所有空格加上#(以及在该行中所有后续文字)都被忽略,除非在一个字符类中或者允许注释并且提高可读性 |
3.5、搜索与替换sub()、subn()
sub(pattern,repl,string,count=0):
sub()和 subn()这两个函数/方法用于实现搜索和替换功能,几乎一样,subn()还返回一个表示替换的总数。
使用 repl 替换所有正则表达式的模式在字符串中出现的位置,除非定义 count,否则就将替换所有出现的位置(另见 subn()函数,该函数返回替换操作的数目)。
import re
r1 = re.sub('X', 'Mr. Smith', 'attn: X\n\nDear X,\n')
print(r1)
'''输出结果:
attn: Mr. Smith
Dear Mr. Smith,
'''
r2 = re.subn('X', 'Mr. Smith', 'attn: X\n\nDear X,\n')
print(r2)
# 输出结果:('attn: Mr. Smith\n\nDear Mr. Smith,\n', 2)
r3 = re.sub('[ae]', 'X', 'abcdef')
print(r3)
# 输出结果:XbcdXf
r4 = re.subn('[ae]', 'X', 'abcdef')
print(r4)
# 输出结果:('XbcdXf', 2)
3.6、限定模式上使用 split()分隔字符串
split(pattern,string,max=0):
根据正则表达式的模式分隔符,split 函数将字符串分割为列表,然后返回成功匹配的列表,分隔最多操作 max 次(默认分割所有匹配成功的位置)。
import re
r1 = re.split(':', 'str1:str2:str3')
print(r1)
# 输出结果:['str1', 'str2', 'str3']
DATA = ('Mountain View,CA 94040','Sunnyvale,CA','Los Altos, 94023','Cupertino 95014', 'Palo Alto CA')
for data in DATA:
print(re.split(',|(?= (?:\d{5}|[A-Z]{2})) ',data)) # 空格紧跟在五个数字(ZIP 编码)或者两个大写字母(美国联邦州缩写)之后,就用 split 语句分割该空格
''' 输出结果:
['Mountain View', 'CA', '94040']
['Sunnyvale', 'CA']
['Los Altos', '', '94023']
['Cupertino', '95014']
['Palo Alto', 'CA']
'''
3.7、 择一匹配(|)符号
择一匹配(|)符号:
用来匹配多个字符串。
bt = 'bat|bet|bit' # 正则表达式模式: bat、bet、bit
m = re.match(bt, 'bat') # 'bat' 是一个匹配
if m is not None:
print(m.group())
# 输出结果:bat
m = re.match(bt, 'blt') # 对于 'blt' 没有匹配
if m is not None:
print(m.group())
# 无输出结果
m = re.match(bt, 'He bit me!') # 不能匹配字符串
if m is not None:
print(m.group())
# 无输出结果
m = re.search(bt, 'He bit me!') # 通过搜索查找 'bit'
if m is not None:
print(m.group())
# 输出结果:bit
3.8、点号(.)
点号(.):
匹配任何单个字符,但不能匹配一个换行符\n 或者非字符(空字符串)。
anyend = '.end'
m = re.match(anyend, 'bend') # 点号匹配 'b'
if m is not None:
print(m.group())
# 输出结果:'bend'
m = re.match(anyend, 'end') # 不匹配任何字符
if m is not None:
print(m.group())
# 无输出结果:
m = re.match(anyend, '\nend') # 除了 \n 之外的任何字符
if m is not None:
print(m.group())
# 无输出结果:
m = re.search('.end', 'The end.')# 在搜索中匹配 ' '
if m is not None:
print(m.group())
# 输出结果:'end'
3.9、创建字符集([ ])
m = re.match('[cr][23][dp][o2]', 'c3po')# 匹配 'c3po'
if m is not None:
print(m.group())
# 输出结果:'c3po'
m = re.match('[cr][23][dp][o2]', 'c2do')# 匹配 'c2do'
if m is not None:
print(m.group())
# 输出结果:'c2do'
m = re.match('r2d2|c3po', 'c2do')# 不匹配 'c2do'
if m is not None:
print(m.group())
# 无输出结果
m = re.match('r2d2|c3po', 'r2d2')# 匹配 'r2d2'
if m is not None:
print(m.group())
# 输出结果:'r2d2'
3.10、重复、特殊字符以及分组
允许.com 前面有一个或者两个名称:
patt = '\w+@(\w+\.)?\w+\.com'
g1 = re.match(patt, '[email protected]').group()
print(g1)
# 输出结果:'[email protected]'
g2 = re.match(patt, '[email protected]').group()
print(g2)
# 输出结果:'[email protected]'
patt = '\w+@(\w+\.)*\w+\.com'
g3 = re.match(patt, '[email protected]').group()
print(g3)
# 输出结果:'[email protected]'
m = re.match('\w\w\w-\d\d\d', 'abc-123')
if m is not None:
print(m.group())
# 输出结果:'abc-123'
m = re.match('\w\w\w-\d\d\d', 'abc-xyz')
if m is not None:
print(m.group())
# 无输出结果:
m = re.match('(\w\w\w)-(\d\d\d)', 'abc-123')
print(m.group()) # 完整匹配
# 输出结果:'abc-123'
print(m.group(1)) # 子组 1
# 输出结果:'abc'
print(m.group(2)) # 子组 2
# 输出结果:'123'
print(m.groups()) # 全部子组
# 输出结果:('abc', '123')
m = re.match('ab', 'ab') # 没有子组
print(m.group()) # 完整匹配
# 输出结果:'ab'
print(m.groups()) # 所有子组
# 输出结果:()
m = re.match('(ab)', 'ab') # 一个子组
print(m.group()) # 完整匹配
# 输出结果:'ab'
print(m.group(1)) # 子组 1
# 输出结果:'ab'
print(m.groups()) # 全部子组
# 输出结果:('ab',)
m = re.match('(a)(b)', 'ab') # 两个子组
print(m.group()) # 完整匹配
# 输出结果:'ab'
print(m.group(1)) # 子组 1
# 输出结果:'a'
print(m.group(2)) # 子组 2
# 输出结果:'b'
print(m.groups()) # 所有子组
# 输出结果:('a', 'b')
m = re.match('(a(b))', 'ab') # 两个子组
print(m.group()) # 完整匹配
# 输出结果:'ab'
print(m.group(1)) # 子组 1
# 输出结果:'ab'
print(m.group(2)) # 子组 2
# 输出结果:'b'
print(m.groups()) # 所有子组
# 输出结果:('ab', 'b')
3.11、匹配字符串的起始和结尾以及单词边界
m = re.search('^The', 'The end.') # 匹配
if m is not None:
print(m.group())
# 输出结果:'The'
m = re.search('^The', 'end. The') # 不作为起始
if m is not None:
print(m.group())
# 无输出结果
m = re.search(r'\bthe', 'bite the dog') # 在边界
if m is not None:
print(m.group())
# 输出结果:'the'
m = re.search(r'\bthe', 'bitethe dog') # 有边界
if m is not None:
print(m.group())
# 无输出结果
m = re.search(r'\Bthe', 'bitethe dog') # 没有边界
if m is not None:
print(m.group())
# 输出结果:'the'
3.12、扩展符号
(?iLmsux) 系列选项:
可以直接在正则表达式里面指定一个或者多个标记,而不是通过 compile()或者其他 re 模块函数。
扩展符号 | 描述 |
---|---|
(?i) | 等同于re.I/IGNORECASE,忽略大小写 |
(?m) | 等同于re.M/MULTILINE ,在目标字符串中实现跨行搜索 |
(?s) | 等同于re.S/DOTALL,点号(.)能够用来表示\n 符号(反之其通常用于表示除了\n 之外的全部字符) |
(?x) | 等同于re.X/VERBOSE,通过抑制在正则表达式中使用空白符(除了在字符类中或者在反斜线转义中)来创建更易读的正则表达式 |
(?:…) | 通过使用该符号,可以对部分正则表达式进行分组,但是并不会保存该分组用于后续的检索或者应用 |
(?P) | 使用一个名称标识符而不是使用从 1 开始增加到 N 的增量数字来保存匹配,如果使用数字来保存匹配结果,我们就可以通过使用\1,\2 …,\N \来检索 |
(?P=name) | 可以在一个相同的正则表达式中重用模式,而不必稍后再次在(相同)正则表达式中指定相同的模式 |
(?=…) | 在目标字符串中实现一个前视匹配,正向前视断言 |
(?!…) | 在目标字符串中实现一个前视匹配, 负向前视断言 |
import re
r1 = re.findall(r'(?i)yes', 'yes? Yes. YES!!')
print(r1)
# 输出结果:['yes', 'Yes', 'YES']
r2 = re.findall(r'(?i)th\w+', 'The quickest way is through this tunnel.')
print(r2)
# 输出结果:['The', 'through', 'this']
r3 = re.findall(r'(?im)(^th[\w ]+)', """
... This line is the first,
... another line,
... that line, it's the best
... """)
print(r3)
# 输出结果:[]
r4 = re.findall(r'th.+', '''
... The first line
... the second line
... the third line
... ''')
print(r4)
# 输出结果:['the second line ', 'the third line ']
r5 = re.findall(r'(?s)th.+', '''
... The first line
... the second line
... the third line
... ''')
print(r5)
# 输出结果:['the second line \n... the third line \n... ']
r6 = re.search(r'''(?x)
\((\d{3})\) 区号
[ ] # 空白符
(\d{3}) # 前缀
- # 横线
(\d{4}) # 终点数字
''', '(800) 555-1212')
if r6 is not None:
print(r6.group())
# 无输出结果
r7 = re.findall(r'http://(?:\w+\.)*(\w+\.com)', 'http://google.com http://www.google.com http://code.google.com')
print(r7)
# 输出结果:['google.com', 'google.com', 'google.com']
r8 = re.search(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})', '(800) 555-1212').groupdict()
print(r8)
# 输出结果:{'areacode': '800', 'prefix': '555'}
# 使用一个类似风格的\g<name>来检索它们
r9 = re.sub(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})', '(\g<areacode>) \g<prefix>-xxxx', '(800) 555-1212')
print(r9)
# 输出结果:(800) 555-xxxx
# 压缩版
r10 = bool(re.match(
r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?P<number>\d{4}) (?P=areacode)-(?P=prefix)-(?P=number)(?P=areacode)(?P=prefix)(?P=number)',
'(800) 555-1212 800-555-1212 18005551212'))
print(r10)
# 输出结果:False
# 普通版
r11 = bool(re.match(r'''(?x)
# match (800) 555-1212, save areacode, prefix, no.
\((?P<areacode>\d{3})\)[ ](?P<prefix>\d{3})-(?P<number>\d{4})
# space
[ ]
# match 800-555-1212
(?P=areacode)-(?P=prefix)-(?P=number)
# space
[ ]
# match 18005551212
1(?P=areacode)(?P=prefix)(?P=number)
''', '(800) 555-1212 800-555-1212 18005551212'))
print(r11)
# 输出结果:True
r12 = re.findall(r'\w+(?= van Rossum)', '''
Guido van Rossum
Tim Peters
Alex Martelli
Just van Rossum
Raymond Hettinger
... ''')
print(r12)
# 输出结果:['Guido', 'Just']
r13 = re.findall(r'(?m)^\s+(?!noreply|postmaster)(\w+)', '''
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
... ''')
print(r13)
# 输出结果:['sales']
r14 = ['%[email protected]' % e.group(1) for e in re.finditer(r'(?m)^\s+(?!noreply|postmaster)(\w+)', '''
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
... ''')]
print(r14)
# 输出结果:['[email protected]']