《Python 3 程序开发指南》 学习笔记
13.1 Python的正则表达式语言
13.1.1 字符与字符类
特殊字符:\.^$?+*{}|
在一个字符类内部,除\外,特殊字符不再具备特殊意义.
当^ 为一个字符类的第一个字符时,表其特殊含义否定。
-表示一个字符范围,如果作为字符类中的第一个字符,就表示一个字面意义上的连字符。
字符类速记(表)
符号 |
含义 |
. |
可以匹配除换行符之外的任意字符,或带re.DOTALL标记的任意字符,或匹配字符内部的字面意义的字符。 |
\d |
匹配一个Unicode数字,或带(等价于的意思?)re.ASCII标记的[0-9] |
\D |
匹配一个Unicode非数字,或带re.ASCII标记的[0-9] |
\s |
匹配Unicode空白,或带re.ASCII标记的[\t\n\r\f\v] |
\S |
匹配Unicode非空白,或带re.ASCII标记的[^\t\n\r\f\v] |
\w |
匹配一个Unicode单词字符,或带re.ASCII标记的[a-zA-Z0-9] |
\W |
匹配一个Unicode非单词字符,或带re.ASCII标记的[^a-zA-Z0-9] |
13.1.2 量词 {m, n} ? + *
{m, n}:m表示该量词必须匹配的最小次数,而n表示最多次数,如e{1,1}, e{2, 2}
如果m, n相同,可以简写为{m}。而量词{0,1}有一种速记形式?.
travell{0,1}ed == travell?ed
正则表达式量词(表)
语法 |
含义 |
e? or e |
贪婪地匹配表达式e的0或1次出现 |
e?? or e{0,1}? |
非贪婪地匹配表达式e e+或{1,}的0次或1次出现,贪婪地匹配表达式e的1次或多次出现 |
e+? or e{1,}? |
非贪婪地匹配表达式e的一次或多次出现 |
e* or e |
贪婪地匹配表达式e的0次或1次出现 |
e*? or e{0,}? |
非贪婪地匹配表达式e地0次或1次出现 |
e |
准确匹配表达式e的m次出现 |
e |
贪婪地匹配表达式e的至少m次出现 |
e{m,}? |
非贪婪地匹配表达式e的至少m次出现 |
e |
贪婪地匹配表达式e的至多n次出现 |
e{,n}? |
非贪婪地匹配表达式e的至多n次出现 |
e |
贪婪地匹配表达式e的至少m次,至多n次出现 |
e+ |
e{1,n}(至少一次),n是量词地可能地最大值,通常是32767 |
e* |
e{0,n}(任意次),n同上 |
e{m,n}? |
非贪婪地匹配表达式e地至少m次,至多n次出现 |
- 默认情况下,所有量词都是贪婪的,量词后面跟随?表示非贪婪。
re.match(r"\d+", "136")
#<re.Match object; span=(0, 3), match='136'>
re.match(r"\d+?", "136")
#<re.Match object; span=(0, 1), match='1'>
re.match(r"\d??", "136")
#<re.Match object; span=(0, 0), match=''>
re.match(r"\d{0,1}?", "136")
#<re.Match object; span=(0, 0), match=''>
re.match(r"\d{0,1}?36", "136")
#<re.Match object; span=(0, 3), match='136'>
re.match(r"\d*?", "136")
#<re.Match object; span=(0, 0), match=''>
re.match(r"\d*?6", "136")
#<re.Match object; span=(0, 3), match='136'>
re.match(r"<img\s+[^>]*?src=\w[^>]*?>", "<img src=dd>")
#<re.Match object; span=(0, 14), match='<img src=dd>'>
组与捕获 ?:可以关闭捕获
()可以进行分组,而交替字符|在多种方案中选取一个的情况下使用。
re.match(r"aircraft|airplane|jet", "airplane")
#<re.Match object; span=(0, 8), match='airplane'>
re.match(r"air(craft|plane)|jet", "airplane")
#<re.Match object; span=(0, 8), match='airplane'>
()在分组的同时进行捕获,以备后用。
for i in range(3):
print(re.match(r"(air(craft|plane)|jet)", "airplane")[i])
#airplane #只要匹配成功,首项都是完整匹配项
#airplane
#plane
try:
for i in range(3):
print(re.match(r"(air(?:craft|plane)|jet)", "airplane")[i]) #?:关闭捕获
except IndexError as err:
print("IndexError: {0}".format(err))
#airplane
#airplane
#IndexError: no such group
s = "eric1 = something\n"\
"eric2 = somewhere\n"\
"eric3 = somebody"
try:
for i in range(10):
print(re.match(r"(\w+ = .+\n?)+", s)[i])
except IndexError as err:
print("IndexError: {0}".format(err))
#eric1 = something
#eric2 = somewhere
#eric3 = somebody
#eric3 = somebody
#IndexError: no such group
s = "eric1 = something\n"\
"eric2 = somewhere\n"\
"eric3 = somebody"
try:
for i in range(10):
print(re.match(r"(?:(\w+) = (.+)\n?)+", s)[i])
except IndexError as err:
print("IndexError: {0}".format(err))
#eric1 = something
#eric2 = somewhere
#eric3 = somebody
#eric3
#somebody
#IndexError: no such group
反向引用 \i 反向引用不能用在字符类内部,即不能用在[]中
re.match(r"(\w+)\s+\1", "eric eric")
#<re.Match object; span=(0, 9), match='eric eric'>
re.match(r"(\w+)(\d?)\s+\1\2", "eric1 eric1")
#<re.Match object; span=(0, 11), match='eric1 eric1'>
re.match(r"(\w+)(\d?)\s+\1\2", "eric1 eric2") # None
命名捕获 (?P...) 反向引用:(?P=name)
- 注意, (?P=name)只是匹配,((?P=name))才能完成捕获
for item in ('name', 'what'):
print(re.match(r"(?P<name>\w+)\s+(?P<what>\w+)", "Pikachu Pokemon")[item])
#Pikachu
#Pokemon
re.match(r"(?P<name>\w+)\s+((?P=name))", "Pikachu Pikachu")[2]
#'Pikachu'
断言与标记
符号 |
含义 |
^ |
在起始处匹配,也可以在带MULTILINE标记的每个换行符后匹配 |
$ |
在结尾处匹配,也可以在MULTILINE标记的每个换行符前匹配 |
\A |
在起始处匹配 |
\b |
在单词边界匹配,受re.ASCII标记影响——在字符类内部,则是backspace字符的转义字符 |
\B |
在非单词边界匹配,受re.ASCII标记影响 |
\Z |
在结尾处匹配 |
(?=e) |
如果表达式e在此断言处匹配,但没有超越此处——称为前瞻或正前瞻,则匹配 |
(?!e) |
如果表达式e在此前言处不匹配,也没有超越此处——称为负前瞻,则匹配 |
(?<=e) |
如果表达式e恰在本断言之前匹配——成为正回顾,则匹配 |
(?<!e) |
如果表达式e恰在本断言之前不匹配——称为负回顾,则匹配 |
- 单词边界,顾名思义就是该位置为单词的边界,比如\b位置之前是一个单词,后面是非单词(比方空格),当然反过来也是可以的。
re.match(r"eric", "eric1")
#<re.Match object; span=(0, 4), match='eric'>
re.match(r"eric\b", "eric1") #None
re.match(r"deric", "deric1")
#<re.Match object; span=(0, 5), match='deric'>
re.match(r"d\beric", "deric1") #None
s = "I'm sorry, my son.\n Actually, actually, she is my daughter, and your sister..."
re.match(r".+(?<=son)", s)
#<re.Match object; span=(0, 17), match="I'm sorry, my son">
s = "I'm sorry, my son.\n Actually, actually, she is my daughter, and your sister..."
re.match(r".+(?<!daugther)", s)
#<re.Match object; span=(0, 18), match="I'm sorry, my son.">
条件性匹配 (?(id)yes_exp) | (?(id)yes_exp|no_exp)
s1 = "src=\"ddd.ddd\""
s2 = "src='ddd.ddd'"
s3 = "src=ddd.ddd"
re.match(r"src=([\"'])([^\"'>]+)\1", s1) # ! s3
#<re.Match object; span=(0, 13), match='src="ddd.ddd"'>
re.match(r"src=([\"'])?([^\"'>]+)(?(1)\1)", s3)
#<re.Match object; span=(0, 11), match='src=ddd.ddd'>
设置标记 (?flags) flags == a | i | m| s | x
符号 |
含义 |
a |
re.ASCII |
i |
re.IGNORECASE |
m |
re.MULTILINE |
s |
re.DOTALL |
x |
re.VERBOSE |
13.2 正则表达式模块
正则表达式模块的函数(表)
语法 |
描述 |
re.compile(r, f) |
返回编译后的正则表达式r,如果指定,就将其标记设置为f |
re.escape(s) |
返回字符串s,其中所有非字母数字的字符都是用反斜线进行了转义处理,因此返回的字符串中没有特殊的正则表达式字符 |
re.findall(r, s, f) |
返回正则表达式r在字符串s中所有非交叠的匹配(如果给定f,就受其值约)。如果正则表达式中有捕获,那么每次匹配都作为一个捕获元组返回 |
re.finditer(r, s, f) |
对正则表达式r在字符串s中每个非交叠的匹配(如果给定f,就受其值约),都返回一个匹配对象 |
re.match(r, s, f) |
如果正则表达式r在字符串s的起始处匹配(如果给定f,就受其制约),就返回一个匹配对象,否则返回None |
re.search(r, s, f) |
如果正则表达式r在字符串s的任意位置处匹配(如果给定f,就受其值约),就返回一个匹配对象,否则返回None |
re.split(r, s, m) |
返回分割字符串s(在正则表达式r每次出现出进行分割)所产生的字符串的列表,至多分割m次(如果没有给定m,就分割尽可能多的次数),如果正则表达式中包含捕获,就被包含在分割的部分之间 |
re.sub(r, x, s, m) |
对正则表达式r的每次匹配(如果给定m,那么至多m次),返回字符串s的一个副本,并将其替换为x——这可以是一个字符串,也可以是一个函数 |
re.subn(r, x, s, m) |
与re.sub()函数相同,区别在于此函数返回一个二元组,其中一项为生成的字符串,一项为代入的次数 |
import re
re.escape("\\") #'\\\\'
s = "eric1 = something\n"\
"eric2 = somewhere\n"\
"eric3 = somebody"
try:
for i in range(10):
print(re.findall(r"(?:(\w+) = (.+)\n?)+", s)[i])
except IndexError as err:
print("IndexError: {0}".format(err))
#('eric3', 'somebody')
#IndexError: list index out of range
#从下面的例子可以明白什么是非交叠匹配
s = "bigbigericbigbig\n"\
"smallsmallericsmallsmall\n"\
"hardharderichardhard"
e = r"""(?x)(?i)
((?P<attr>big|small)(eric)\2)
"""
e = re.compile(e)
re.findall(e, s) #[('bigericbig', 'big', 'eric'), ('smallericsmall', 'small', 'eric')]
s = "eric1 = something\n"\
"eric2 = somewhere\n"\
"eric3 = somebody"
re.split(r"\n", s)
#['eric1 = something', 'eric2 = somewhere', 'eric3 = somebody']
正则表达式模块的标记(表)
标记 |
含义 |
re.A or re.ASCII |
使\b,\B,\s,\S,\w,\W都假定字符串为ASCII,默认为这些字符类的速记法,依赖于Unicode规范 |
re.I or re.IGNORECASE |
使正则表达式以大小写不敏感的方法进行匹配 |
re.M or re.MULTILINE |
使^在起始处并在每个换行符后匹配,使$在结尾处但在每个换行符之前匹配 |
re.S or re.DOTALL |
使.匹配每个字符,包括换行符 |
re.X or re.VERBOSE |
使空白和注释包含在匹配中 |
s = "eric1=something\n"\
"eric2 = somewhere\n"\
"Eric3 = somebody"
re.findall(r"eric", s, re.I)
#['eric', 'eric', 'Eric']
e = r"""(?x)
(?P<eric>eric)
"""
e = re.compile(e)
e.findall(s)
#['eric', 'eric']
正则表达式对象方法(表)
rx表正则表达式,下面的方法其实上面都有提到过。
语法 |
描述 |
rx.findall(s, start, end) |
返回字符串s中(或s的start:end分片中)正则表达式的所有非交叠的匹配,如果正则表达式有捕获,那么每次匹配是返回一个捕获元组 |
rx.finditer(sstart, end) |
对字符串s中(或s的start:end分片中)的每个非交叠匹配,返回一个匹配对象 |
rx.flags |
正则表达式编译时设置的标记 readonly |
rx.groupindex |
一个字典,其键位捕获组名,值为捕获组编号,如果没有使用名称就为空 |
rx.match(s, start, end) |
如果正则表达式在字符串s的起始处(或s的start:end分片起始处)匹配,就返回一个匹配对象,否则返回None |
rx.pattern |
正则表达式被编译时使用的字符串 |
rx.search(s, start, end) |
如果正则表达式在字符串s的任意位置(或s的start:end分片中的任意位置)匹配,就返回一个匹配对象,否则返回None |
rx.split(s, m) |
返回字符串列表,其中每个字符串都源自对字符串s的分割(在正则表达式的每次匹配处),但至多有m个分割(如果没有给定m,则可以有尽可能多的分割);如果正则表达式有捕获,就包含在列表中俩个分割之间 |
rx.sub(x, s, m) |
返回字符串s的副本,其中每个(或至多m个,如果给定)匹配出使用x(可以时字符串或函数)进行替换 |
rx.subn(x, s, m) |
与rx.sub()相同,区别在于返回的是二元组,其中一项时结果字符串,一项时所做替换的个数 |
e.groupindex
#mappingproxy({'eric': 1})
e.pattern
#'(?x)\n (?P<eric>eric)\n'
匹配对象的属性与方法(表)
m 匹配对象——finditer, match等方法可能返回匹配对象
语法 |
描述 |
m.end(g) |
返回组g(如果给定)在文本中匹配的终点索引位置,对组0,则表示整体匹配;如果匹配中不包含改组,就返回-1 |
m.endpos |
搜索的终点索引位置(文本的重点,或赋予match()或search()的end |
m.expand(s) |
返回字符串s,并将其中的捕获标志(\1, \2, \g等类似的标志)用相应的捕获替代 经测试如果s== r"..."上述可行,如果s是普通的字符串,应当用:\1, \2, \g,没错除了\g外需要转义 |
m.group(g, ...) |
返回编号的或命名的组g,如果给定的不止一个,就返回相应的捕获组成的元组(组0表示整体匹配) |
m.groupdict(default) |
返回一个字典,其中存放所有命名的捕获组,组名作为键,捕获作为值;如果给定了default参数,就将其用作那些不参与匹配的捕获组的值 |
m.groups(default) |
返回包含所有捕获组的元组,从1开始;如果给定default,就将其用作那些不参与匹配的捕获组的值 |
m.lastgroup |
匹配的,编号最高的捕获组的名称,如果不存在或没使用名称,就返回None |
m.lastindex |
匹配的,编号最高的捕获组的编号,如果没有就返回None |
m.pos |
搜索的起始索引位置(文本的起始处,或赋予match()或search()的start) |
m.span(g) |
如果给定g,就返回组g在文本中匹配的起始索引位置与结尾索引位置(对组0,则是整体匹配);如果改组不参加匹配,就返回(-1,-1) |
m.start(g) |
如果给定g,就返回组g在文本中匹配的起始索引位置(对组0,则是整体匹配);如果改组不参加匹配,就返回01 |
m.string |
船体给match()或search()的字符串 |
s = "bigbigericbigbig\n"\
"smallsmallericsmallsmall\n"\
"hardharderichardhard"
e = r"""(?x)(?i)
((?P<attr>big|small)(eric)\2)
"""
e = re.compile(e)
ms = re.finditer(e, s)
for m in list(ms):
print(m.end(2), m.endpos)
#6 62
#27 62
s2 = "\g<attr>pig"
s3 = "\\2cat"
s4 = r"\2cat"
ms = re.finditer(e, s)
print(next(ms).expand(s2))
m = next(ms)
print(m.expand(s3), m.expand(s4))
#bigpig
#smallcat smallcat
m.group(), m.group(1), m.group(1, 2, 3)
#('smallericsmall', 'smallericsmall', ('smallericsmall', 'small', 'eric'))
m.groupdict()
#{'attr': 'small'}
m.groups()
#('smallericsmall', 'small', 'eric')
m.lastgroup #None
m.lastindex #1
m.span(), m.span(1), m.span(2), m.span(3)
#((22, 36), (22, 36), (22, 27), (27, 31))
m.string
#'bigbigericbigbig\nsmallsmallericsmallsmall\nhardharderichardhard'