Python Revisited Day 13 (正则表达式)

时间:2023-03-08 22:19:32

《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{0,1} 贪婪地匹配表达式e的0或1次出现
e?? or e{0,1}? 非贪婪地匹配表达式e e+或{1,}的0次或1次出现,贪婪地匹配表达式e的1次或多次出现
e+? or e{1,}? 非贪婪地匹配表达式e的一次或多次出现
e* or e{0,} 贪婪地匹配表达式e的0次或1次出现
e*? or e{0,}? 非贪婪地匹配表达式e地0次或1次出现
e{m} 准确匹配表达式e的m次出现
e{m,} 贪婪地匹配表达式e的至少m次出现
e{m,}? 非贪婪地匹配表达式e的至少m次出现
e{,n} 贪婪地匹配表达式e的至多n次出现
e{,n}? 非贪婪地匹配表达式e的至多n次出现
e{m,n} 贪婪地匹配表达式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'