正则表达式(regular expression),是匹配文本字段的模式。其设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。若想使用正则表达式处理字符串,必须使用支持正则表达式的工具。python提供了对正则表达式的支持。
下图展示了适用于python的正则表达式元字符及说明:
python提供re模块实现对正则表达式的支持。
1. re模块函数
1.1 match()函数
格式:re.match(pattern, string, flags=0)
- 从字符串开头匹配,若匹配到就返回一个相应的匹配对象(match对象) 。 如果没有匹配,就返回 None。
- pattern为正则表达式,string为要匹配的字符串。match()方法默认只匹配字符串开头(即从开头开始匹配)
- 实际使用一般先判断是否返回match对象(是否成功匹配),再进行相关操作。
match对象支持以下方法:
Match.start() Match.end() Match.span() :分别返回匹配结果的开始索引,结束索引,整体索引。
-
Match.group([group1, ...])
- 返回一个或者多个匹配到的结果。match对象内部存储了正则表达式匹配到的结果,并将每个分组匹配到的结果从1开始编号。
- 如果只有一个参数,就只返回该分组对应的匹配结果(一个字符串);如果有多个参数,就返回多个参数对应的匹配结果组成的元组;若没有参数或者参数为0,会将整个匹配结果返回。
Match.groups(default=None):返回一个元组,包含每个分组匹配到的结果。
# 正则表达式:re.match()
import re
str1 = "one2two33four"
str2 = "12345..7890"
# 正则表达式:匹配两边若干个数字,中间若干个.组成的字符串
pattern = r"(\d+)\.*(\d+)"
# 返回匹配对象
result1 = re.match(pattern, str1
result2 = re.match(pattern, str2)
print(result1) # 未匹配到返回None
print(result2) # 匹配到返回match对象
print(result2.group()) # 返回正则表达式匹配到的结果
print(result2.group(0)) # 返回正则表达式匹配到的结果
print(result2.group(1, 2)) # 返回正则表达式中两个分组匹配到的结果(元组)
print(result2.group(1)) # 返回正则表达式中第一个分组匹配到的结果
print(result2.group(2)) # 返回正则表达式中第二个分组匹配到的结果
print(result2.groups()) # 返回正则表达式中每个分组匹配到的结果(元组)
# 执行结果
None
<re.Match object; span=(0, 11), match='12345..7890'>
12345..7890
12345..7890
('12345', '7890')
12345
7890
('12345', '7890')
1.2 search()函数
格式:re.search(pattern, string, flags=0)
- 搜索整个字符串进行匹配,若匹配到就返回一个相应的匹配对象(match对象) 。 如果没有匹配,就返回 None。
- 注意点:只会返回匹配到的第一个结果。
# 正则表达式:re.search()
import re
str1 = "one123...456seven789...345"
# 正则表达式:匹配匹配两边若干个数字,中间若干个.组成的字符串
pattern = r"(\d+)\.*(\d+)"
result = re.search(pattern, str1)
print(result)
print(result.group())
print(result.group(0))
print(result.group(1, 2))
print(result.group(1))
print(result.group(2))
print(result.groups())
# 执行结果
<re.Match object; span=(3, 12), match='123...456'>
123...456
123...456
('123', '456')
123
456
('123', '456')
1.3 findall()函数
格式:re.findall(pattern, string, flags=0)
- 搜索整个字符串,返回正则表达式匹配到的所有结果组成的列表形式。
- 如果正则表达式里面存在多个分组,则每个匹配到的结果是表达式中每个分组匹配到的字符串组成的元组形式。
# 正则表达式:re.findall()
import re
str1 = "123four567eight"
str2 = "one123...456seven789...345"
# 正则表达式
pattern1 = r"\d+"
pattern2 = r"(\d+)\.*(\d+)"
result1 = re.findall(pattern1, str1)
result2 = re.findall(pattern2, str2)
print(result1)
print(result2)
# 执行结果
['123', '567']
[('123', '456'), ('789', '345')]
1.4 sub()函数
格式:re.sub(pattern, repl, string, count=0, flags=0)
使用表达式对字符串进行匹配,将每一个匹配到的结果使用repl进行替换,返回替换后的结果
-
参数解析:
- pattern:正则表达式;
- repl:可为一个字符串或者函数;若为一个字符串,里面的转义字符会被处理(原始字符串除外);若为一个函数,该函数接收匹配到的对象为参数,返回一个替换后的字符串
- repl也可使用\number形式引用分组匹配到的字符串进行替换
# 正则表达式:re.sub()
import re
def deal(matchobj):
# 根据匹配结果的长度返回不同的替换字符串
if len(matchobj.group()) <= 6:
return "---------"
else:
return "*********"
str1 = "one12..56seven789...345"
# 正则表达式:此处会匹配到两个结果会对两个结果进行替换
pattern = r"(\d+)\.*(\d+)"
result1 = re.sub(pattern, "_replace_string_", str1) # 使用字符串替换
result2 = re.sub(pattern, deal, str1) # 使用函数处理替换
result3 = re.sub(pattern, r"\2 \1", str1)
print(result1)
print(result2)
print(result3)
# 执行结果
one_replace_string_seven_replace_string_
one---------seven*********
one56 12seven345 789
1.5 subn()函数
格式:re.subn(pattern, repl, string, count=0, flags=0)
- 作用和sub()相似,返回一个元组(str, int),第一个元素为替换后的字符串,第二个为替换的次数
import re
result01 = re.subn(r'(\w+) (\w+)', r'hello world', 'hello 123, hello 456')
result02 = re.subn(r'(\w+) (\w+)', r'\2 \1', 'hello 123, hello 456')
print(result01)
print(result02)
# 结果
('hello world, hello world', 2)
('123 hello, 456 hello', 2)
1.6 split()函数
格式:re.split(pattern, string, maxsplit=0, flags=0)
- 使用正则表达式匹配到的字符串进行分割,返回列表形式
- 若正则表达式里面含有分组,则分组匹配到的字符也会包含在列表里面
# 正则表达式:re.split()
import re
str1 = "info:xiaoZhang 33 shandong"
pattern = r":| "
result = re.split(pattern, str1)
print(result)
# 执行结果
['info', 'xiaoZhang', '33', 'shandong']
2. 正则表达式对象的方法
可使用re.compile()函数将正则表达式编译为哟个正则表达式对象,然后使用对象的方法实现字符串匹配。
2.1 compile()函数
该函数用于编译正则表达式,形式如:re.compile(pattern, flags=0)。返回一个pattern对象。
- 使用compile函数将正则表达式编译为一个pattern对象(模式)
- 通过pattren对象提供的方法对文本进行匹配,获得匹配结果(一个match对象或None)
- 利用match对象的属性和方法获得信息(匹配的子串,索引位置等)
2.2 match()方法
格式:pattern.match(string: AnyStr, pos: int = ..., endpos: int = ...)
- pos和endpos参数指定匹配的范围即起始和结束位置。它是一次匹配,即找到了一个结果就返回,而不是查找所有的匹配结果。匹配成功时,返回一个match对象,否则返回None。
- 实际使用一般先判断是否返回match对象(是否成功匹配),再进行相关操作。
# match()方法
import re
str1 = "12345..7890"
str2 = "zjbiafbia456....89764"
pattern = re.compile(r"(\d+)\.*(\d+)")
print(pattern)
result1 = pattern.match(str1)
result2 = pattern.match(str2, 9, 21) # 制定匹配的范围
print(result1)
print(result2)
print(result1.start()) # 打印匹配结果的开始索引
print(result1.end()) # 打印匹配结果的结束索引
print(result1.group()) # 打印匹配的结果
print(result1.group(0)) # 打印匹配的结果
print(result1.group(1)) # 打印打印匹配结果中的第一个分组结果
print(result1.group(2)) # 打印打印匹配结果中的第二个分组结果
print(result2.group())
print(result2.group(0))
print(result2.group(1))
print(result2.group(2))
# 执行结果
re.compile('(\\d+)\\.*(\\d+)')
<re.Match object; span=(0, 11), match='12345..7890'>
<re.Match object; span=(9, 21), match='456....89764'>
0
11
12345..7890
12345..7890
12345
7890
456....89764
456....89764
456
89764
2.3 search()方法
格式:Pattern.search(string[, pos[, endpos]])
- 搜索整个字符串,进行匹配,匹配到一个结果就返回。
- pos和endpos参数指定匹配的范围即起始和结束位置。
# search()方法
import re
str1 = "one123...456seven789...345"
# 正则表达式:匹配匹配两边若干个数字,中间若干个.组成的字符串
pattern = re.compile(r"(\d+)\.*(\d+)")
result = pattern.search(str1)
print(result)
print(result.start()) # 返回匹配字符串的开始索引
print(result.end()) # 返回匹配字符串的结束索引
print(result.group()) # 返回正则表达式匹配到的结果
print(result.group(0)) # 返回正则表达式匹配到的结果
print(result.group(1, 2)) # 返回正则表达式中两个分组匹配到的结果(元组)
print(result.group(1)) # 返回正则表达式中第一个分组匹配到的结果
print(result.group(2)) # 返回正则表达式中第二个分组匹配到的结果
print(result.groups()) # 返回正则表达式中每个分组匹配到的结果(元组)
# 执行结果
<re.Match object; span=(3, 12), match='123...456'>
3
12
123...456
123...456
('123', '456')
123
456
('123', '456')
2.4 findall()方法
格式:Pattern.findall(string[, pos[, endpos]])
- 搜索字符串内的所有符合正则表达式的子串,以列表形式返回。
- pos和endpos参数指定匹配的范围即起始和结束位置。
# findall()方法
import re
str1 = "123four567eight"
str2 = "one123...456seven789...345"
# 正则表达式
pattern1 = re.compile(r"\d+")
pattern2 = re.compile(r"(\d+)\.*(\d+)")
result1 = pattern1.findall(str1)
result2 = pattern2.findall(str2)
print(result1)
print(result2)
# 执行结果
['123', '567']
[('123', '456'), ('789', '345')]
2.5 split()方法
格式:Pattern.split(string, maxsplit=0) 类似于re.split()方法,分割字符串。
# split()方法
import re
str1 = "info:xiaoZhang 33 shandong"
pattern = re.compile(r":| ")
result = pattern.split(str1)
print(result)
# 执行结果
['info', 'xiaoZhang', '33', 'shandong']
2.6 sub()方法
- 使用字符串或函数返回的字符串替换pattern对象匹配到的字符串,等同于re.sub()函数。
# sub()方法
import re
def deal(matchobj):
# 根据匹配结果的长度返回不同的替换字符串
if len(matchobj.group()) <= 6:
return "---------"
else:
return "*********"
str1 = "one12..56seven789...345"
# 正则表达式:此处会匹配到两个结果会对两个结果进行替换
pattern = re.compile(r"(\d+)\.*(\d+)")
result1 = pattern.sub("_replace_string_", str1) # 使用字符串替换
result2 = pattern.sub(deal, str1) # 使用函数处理替换
result3 = pattern.sub(r"\2 \1", str1)
print(result1)
print(result2)
print(result3)
# 执行结果
one_replace_string_seven_replace_string_
one---------seven*********
one56 12seven345 789
subn()
使用字符串或函数返回的字符串替换pattern对象匹配到的字符串,返回一个元组(替换后的字符串, 替换的次数),等同于re.subn()函数。
import re
pattern = re.compile(r'(\w+) (\w+)')
word = 'hello 123, hello 456'
def func(word):
return 'hi' + ' ' + word.group(1) # 返回一个字符串用于替换
print(pattern.subn(r'hello world', word)) # 使用字符串替换
print(pattern.subn(r'\2 \1', word)) # 引用匹配到的子串的分组进行替换
print(pattern.subn(func, word)) # 使用函数返回一个字符串用于替换
print(pattern.subn(func, word, 1)) # 指定替换的次数
# 结果
('hello world, hello world', 2)
('123 hello, 456 hello', 2)
('hi hello, hi hello', 2)
('hi hello, hello 456', 1)
从上面可以看出re模块有两种使用方式:
- 将正则表达放入re模块的各函数执行
- 使用compile函数预编译正则表达式产生pattern对象,使用pattern对象的各个方法
如何选择使用方式呢?如果一个正则表达式需要使用多次,出于效率的考虑,可预编译正则表达式,即第二种方式。
3. 匹配中文
中文的 unicode 编码范围 主要在 [\u4e00-\u9fa5],这里说主要是因为这个范围并不完整,比如没有包括全角(中文)标点,不过,在大部分情况下,应该是够用的。
import re
word = '你好,hello,世界'
result = re.findall(r'[\u4e00-\u9fa5]+', word)
print(result)
# 结果
['你好', '世界']
4. 贪婪匹配
python中,正则表达式默认为贪婪匹配,即匹配尽可能多的字符。
import re
word = 'aa<div>test1</div>bb<div>test2</div>cc'
result = re.findall(r'<div>.+</div>', word)
print(result)
result = re.findall(r'<div>.+?</div>', word)
print(result)
# 结果
['<div>test1</div>bb<div>test2</div>']
['<div>test1</div>', '<div>test2</div>']
由于是贪婪匹配,在成功匹配第一个</div>后,会继续向右尝试匹配,查看是否还有更长的可以成功匹配的子串。若想非贪婪匹配,可在+后面添加一个?。