Python进阶–模块-re
1. 正则表达式
正则表达式,在字符串处理业务中经常会用到。这里对正则表达式的匹配规则不再赘述,我们仅介绍Python的re
模块。
2. findall
2.1 方法解析
findall
是re
模块中常用的方法,与其他方法不同的是他的返回值是一个列表。
findall(pattern, string, flags=0):
return list()
- findall:正则匹配模型
- string:要匹配的字符串
- flag:匹配模式
2.2 举个栗子
import re
s = "aAbBcCdD"
f = re.findall(r"[a-z][A-Z][a-z]", s)
print(f)
['aAb', 'cCd']
观察结果我们不难发现:并没有匹配到bBc,对其分析:
- 匹配时首先匹配到
aAb
并加入返回的列表中 - 继续匹配,此时指针指到字符B(接着匹配,而不重头再来),字符B不符合,不断后移。
- 匹配到字符串
bCc
。
由此得出结论:
匹配成功后,再次匹配时,会从已匹配字符串的后一位开始匹配(并不重新开始)。简言之,已经匹配过的字符串不会参与到后续匹配。
2.3 匹配模式
findall
方法有参数flag表示匹配模式,其含义如下:
re.A 或 re.ASCII | 使用ASCII字符集进行匹配(不常用) |
---|---|
re.I 或 re.IGNORECASE | 忽略大小写匹配 |
re.L 或 re.LOCALE | 使用当前预定字符类 \w \W \b \B \s \S 取决于当前区域设定(不常用) |
re.U 或 re.UNICODE | 使用Unicode字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性(不常用) |
re.M 或 re.MULTILINE | 多行匹配,使"^","$"可以在每一行中都进行匹配 |
re.S 或 re.DOTALL | 使 “.” 可以匹配换行符"\r","\n" |
re.X 或 re.VERBOSE | 去掉正则表达式中的所有空格符(不常用) |
import re
s = """
name: danny
age: 14
"""
f = re.findall(r": (.*?).age: (.*?)$", s)
f_s = re.findall(r": (.*?).age: (.*?)\b$", s, re.S)
f_n = re.findall(r": (.*?)\nage: (.*?)\b$", s, re.S)
print("f: ",f)
print("f_s: ",f_s)
print("f_n: ",f_n)
f: []
f_s: [('danny', '14')]
f_n: [('danny', '14')]
首行的danny
和次行的age
之间有换行符\n
,正常模式下元符号.
无法匹配到\n \r
等。但是,使用模式re.S
后,元符号.
可以匹配到\n \r
。
故f
的结果为空,f_s
匹配到字符串,f_n
是对照组。
3.4 分组
3.4.1 简介
什么是组?简单来说当我们想要从符合匹配规则的字符串中提取一段或多段字符串时,这一段段字符串就是一个个组。例如从aAbBcC
中使用r"[a-z][A-Z]"
取出aA
,bB
,cC
,这时我们就说,结果有三个组。
3.4.2 对比
设计对照组如下:
string = "a1b2c3d4"
no_group = r"\w\d"
has_group = r"(\w)\d"
mul_group = r"(\w)(\d)"
nest_group = r"((\w)(\d))"
print("无分组:", re.findall(no_group, string))
print("有分组:", re.findall(has_group, string)) #将“组”内信息用圆括号包裹
print("多分组:", re.findall(mul_group, string))
print("嵌套分组:", re.findall(nest_group, string))
无分组: ['a1', 'b2', 'c3', 'd4']
有分组: ['a', 'b', 'c', 'd']
多分组: [('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')]
嵌套分组: [('a1', 'a', '1'), ('b2', 'b', '2'), ('c3', 'c', '3'), ('d4', 'd', '4')]
-
无分组时,从符合的字符串中返回匹配结果,如字符串“a1”。
-
有分组时,仅返回组内成分(圆括号包含的就是组的范围),如字符串"a"。
-
多个分组时,将所有组封入元祖,每个元祖代表一个匹配到的字符串的结果(如
('a', '1')
),再与其他结果(如('b', '2')等等
)一同封入列表返回 -
嵌套分组时由外到内,将每个组依序封入元祖。上例中依序将组
((\w)(\d)),(\w),(\d)
再次强调:匹配时先依据匹配规则找到匹配的字符串(如’a1’),再对字符串(“a1”)做分组处理,组依于匹配到的字符串而进行操作。分组其实就是说清楚,告诉机器本山人要的是匹配到的字符串的哪一部分。
3.4.3 打开分组
使用?:
可以打开分组,本方法仅适用于不返回re.Match对象的方法,如findall
string = "a1b2c3d4"
no_group = r"\w\d"
has_group = r"(\w)\d"
open_group = r"(?:\w)\d"
print("无分组:", re.findall(no_group, string))
print("有分组:", re.findall(has_group, string))
print("打开分组:", re.findall(open_group, string))
无分组: ['a1', 'b2', 'c3', 'd4']
有分组: ['a', 'b', 'c', 'd']
打开分组: ['a1', 'b2', 'c3', 'd4']
3. match
3.1 方法解析
match方法用于从字符串头开始匹配符合规则的子字符串,注意是从头开始
def match(pattern, string, flags=0):
return re.Match()
- match方法同样接受匹配模式flag字段。
3.2 举个栗子
s = "hello world hello Python"
r = r"h\w+\s\w+"
res = re.match(r, s) #只能匹配到 hello world,因为match方法只从字符串开头开始匹配,不匹配其他位置。
print(res.group())
hello world
- 从起始位置起,开始匹配,匹配到则返回Match对象,反之则返回None
4. search
4.1 方法解析
search,浏览全部字符串,匹配第一符合规则的字符串,浏览整个字符串去匹配第一个,未匹配成功返回None。
def search(pattern, string, flags=0):
return re.Match
4.2 举个栗子
s = '''
<li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/">公司</a></li>
<li class=""><a ka="header-job" href="https://zhiwei url/">职位</a></li>
<li class=""><a ka="header_brand" href="https://gongsi url/">公司</a></li>
'''.strip()
r = r'.*?job" href="(.*?)/".*?brand" href="(.*?)/"'
res = re.search(r, s, re.S)
print("未分组:", res.group(0))
print("组1内容:", res.group(1))
未分组: <li class=""><a ka="header-job" href="https://zhiwei url/">职位</a></li>
<li class=""><a ka="header_brand" href="https://gongsi url/"
组1内容: https://zhiwei url
- 只匹配第一符合的字符串,所以后面的
https://zhiwei url
不会匹配。
5. Match对象
5.1 简介
re模块中,方法findall返回的是列表对象,可以索引取值,但方法match和search返回值是re.Match对象 。注:为了方便,我们称之为Match对象,全称为:_sre.SRE_Match。
s = "hello world hello Python"
r = r"h\w+\s\w+"
res = re.match(r, s)
print(type(res))
print(dir(res))
<class '_sre.SRE_Match'>
[
'group',
'groupdict',
'groups',
'lastgroup',
'start'
......
]
5.2 使用
关于**_sre.SRE_Match**对象(以下简称Match对象)的使用,如下所示:
属性和方法 | 说 明 |
---|---|
Pos | 搜索的开始位置 |
Endpos | 搜索的结束位置 |
String | 搜索的字符串 |
Re | 当前使用的正则表达式的对象 |
Lastindex | 最后匹配的组索引 |
Lastgroup | 最后匹配的组名 |
group(index=0) | 某个分组的匹配结果。如果index等于0,便是匹配整个正则表达式 |
groups() | 所有分组的匹配结果,每个分组的结果组成一个列表返回 |
Groupdict() | 返回组名作为key,每个分组的匹配结果座位value的字典 |
start([group]) | 获取组的开始位置 |
end([group]) | 获取组的结束位置 |
span([group]) | 获取组的开始和结束位置 |
expand(template) | 使用组的匹配结果来替换模板template中的内容,并把替换后的字符串返回 |
1. group
group方法用于分组(圆括号中的内容)
#match.group([group1, ...])
- 如果组中组出的参数0,表示不分组,则返回整个匹配的值。
- 如果组中组出的参数是负数或超出组的最大长度,则给出越界错误。
- 参数可以是两个值,返回元组,元组内包含对应“组”的字符串。
- 未匹配到值的组结果为None。
s = '''
<li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/">公司</a></li>
'''.strip()
r = r'.*?job" href=(.*?)/".*?brand" href=(.*?)/"'
res = re.match(r, s, re.S)
print("未分组:", res.group()) #默认参数为0,即返回所匹配到的字符串,不分组
print("组1内容:", res.group(1))
print("组1,2内容:", res.group(1, 2))
未分组: <li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/"
组1内容: "https://www.zhipin.com/job_detail
组1,2内容: ('"https://www.zhipin.com/job_detail', '"https://www.zhipin.com/gongsi')
2. groups
方法groups以元祖形式返回所有组。
s = '''
<li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/">公司</a></li>
'''.strip()
r = r'.*?job" href=(.*?)/".*?brand" href=(.*?)/"'
res = re.match(r, s, re.S)
print("所有组:", res.groups())
所有组: ('https://www.zhipin.com/job_detail', 'https://www.zhipin.com/gongsi')
3. groupdict
方法groupdict返回命名过的组,未命名的不管。
s = '''
<li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/">公司</a></li>
'''.strip()
r = r'.*?job" href="(?P<zhiwei>.*?)/".*?brand" href="(.*?)/"' #组1命名为zhiwei返回,组2未命名不返回
res = re.match(r, s, re.S)
print("命名组:", res.groupdict())
命名组: {'zhiwei': 'https://www.zhipin.com/job_detail'}
- 命名通过
?P<name>
完成 - 只返回命名过的
4. start & end
方法start返回对应组的起始字符在初始字符串string中的位置。
方法start返回对应组的结束字符在初始字符串string中的位置。
def start(group=0):
return start_index
def end(group=0):
return end_index
- group:组的编号
s = '''
<li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/">公司</a></li>
'''.strip()
r = r'.*?job" href="(?P<zhiwei>.*?)/".*?brand" href="(.*?)/"' #组1命名为zhiwei返回,组2未命名不返回
res = re.match(r, s, re.S)
print("组1开始位置:", res.start(1))
print("组2结束位置:", res.end(2))
print("结束位字符:", s[163])
组1开始位置: 38
组2结束位置: 163
结束位字符: /
- 方法end返回的结束位字符索引值是真实索引值+1
5. span
方法span返回组的起始位置和结束位置构成的元祖,即该组的索引范围。
s = '''
<li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/">公司</a></li>
'''.strip()
r = r'.*?job" href="(?P<zhiwei>.*?)/".*?brand" href="(.*?)/"' #组1命名为zhiwei返回,组2未命名不返回
res = re.match(r, s, re.S)
print("组1索引范围:", res.span(1))
print("索引范围对应的字符串:", eval("s[{0}:{1}]".format(*res.span(1)))) #取出该范围的字符串,等价于r[38:71]
print("组1内容:", res.group(1))
组1索引范围: (38, 71)
索引范围对应的字符串: https://www.zhipin.com/job_detail
组1内容: https://www.zhipin.com/job_detail
- 方法span返回的索引范围可直接用于提取字符串,即结束位置(71)其实是末尾字符(“l”)的索引值+1。其实返回的是(res.start(1), res.end(1))
6 expand
方法expand作用:在参数字符串中,将特定内容替换为组的内容,类似于字符串格式化。
s = '''
<li class=""><a ka="header-job" href="https://www.zhipin.com/job_detail/">职位</a></li>
<li class=""><a ka="header_brand" href="https://www.zhipin.com/gongsi/">公司</a></li>
'''.strip()
r = r'.*?job" href="(?P<zhiwei>.*?)/".*?brand" href="(.*?)/"' #组1命名为zhiwei返回,组2未命名不返回
res = re.match(r, s, re.S)
str_expand = r"""
公司:\2; #\2 会被替换为组2内容
公司:\g<2> #\g<2> 会被替换为组2内容
职位:\g<zhiwei> #\g<zhiwei> 会被替换为组名为 zhiwei 的组的内容
"""
print("expand替换结果:", res.expand(str_expand))
expand替换结果:
公司:https://www.zhipin.com/gongsi;
公司:https://www.zhipin.com/gongsi
职位:https://www.zhipin.com/job_detail
- 替换标志:
\组编号
,\g<组编号>
,\g<组名>