2. search函数
使用语法:
(1)re.search(pattern, string, flags=0)
这个函数前面已经讲解过了。
(2)Pattern对象:search(string[, pos[, endpos]])
例子:
import re pattern = re.compile(r'd+') m = pattern.search('one12twothree34four') print(m.group()) # 12 print(m.groups()) # () m = pattern.search('one12twothree34four', 10, 30) print(m.group()) # 34 print(m.groups()) # ()
3. findall函数
使用语法:
(1)findall(string[, pos[, endpos]])
这个函数前面已经讲解过了。
(2)Pattern对象:findall(string[, pos[, endpos]])
findall 以列表形式返回全部能匹配的子串,如果没有匹配,则返回一个空列表。
例子:
import re pattern = re.compile(r'd+') s1 = pattern.findall('hello world 123 456 789') s2 = pattern.findall('one12twothree34four56', 0, 15) print(s1) # ['123', '456', '789'] print(s2) # ['12', '34']
4. finditer函数
使用语法:
(1)re.finditer(pattern, string, flags=0)
这个函数前面已经讲解过了。
(2)Pattern对象:finditer(string[, pos[, endpos]])
finditer 函数与 findall 类似,但是它返回每一个匹配结果(Match 对象)的迭代器。
例子:
import re pattern = re.compile(r'd+') s1 = pattern.finditer('hello world 123 456 789') s2 = pattern.finditer('one12twothree34four56', 0, 15) for m in s1: print(m.group()) for m in s2: print(m.group())
5. split函数
使用语法:
(1)re.split(pattern, string[, maxsplit=0, flags=0])
这个函数前面已经讲解过了。
(2)Pattern对象:split(string[, maxsplit]])
maxsplit 可指定分割次数,不指定将对字符串全部分割。
例子:
import re s = 'a,1;b 2, c' m = re.compile(r'[s,;]+') print(m.split(s)) # ['a', '1', 'b', '2', 'c'] m = re.compile(r'[s,;]+') print(m.split(s, maxsplit=1)) # ['a', '1;b 2, c']
6. sub函数
使用语法:
(1)re.sub(pattern, repl, string, count=0, flags=0)
这个函数前面已经讲解过了。
(2)Pattern对象:sub(repl, string[, count])
当repl为字符串时,可以用id的形式引用分组,但不能使用编号0;当repl为函数时,返回的字符串中不能再引用分组。
例子:
import re pattern = re.compile(r'(w+) (w+)') s = 'ni 123, hao 456' def func(m): return 'hi' + ' ' + m.group(2) print(pattern.findall(s)) # [('ni', '123'), ('hao', '456')] print(pattern.sub('hello world', s)) # hello world, hello world print(pattern.sub(r' ', s)) # 123 ni, 456 hao print(pattern.sub(func, s)) # hi 123, hi 456 print(pattern.sub(func, s, 1)) # hi 123, hao 456
7. subn函数
subn和sub类似,也用于替换操作。使用语法如下:
Pattern对象:subn(repl, string[, count])
返回一个元组,元组第一个元素和sub函数的结果相同,元组第二个元素返回替换次数。
例子:
import re pattern = re.compile(r'(w+) (w+)') s = 'ni 123, hao 456' def func(m): return 'hi' + ' ' + m.group(2) print(pattern.findall(s)) # [('ni', '123'), ('hao', '456')] print(pattern.subn('hello world', s)) # ('hello world, hello world', 2) print(pattern.subn(r' ', s)) # ('123 ni, 456 hao', 2) print(pattern.subn(func, s)) # ('hi 123, hi 456', 2) print(pattern.subn(func, s, 1)) # ('hi 123, hao 456', 1)
小结
1 使用Pattern对象的match、search、findall、finditer等函数可以指定匹配字符串的起始位置。
2 对re模块的两种使用方式进行对比:
- 使用 re.compile 函数生成一个 Pattern 对象,然后使用 Pattern 对象的一系列方法对文本进行匹配查找;
- 直接使用 re.match, re.search 和 re.findall 等函数直接对文本匹配查找。
例子:
import re # 方法1 # 将正则表达式先编译成 Pattern 对象 pattern = re.compile(r'd+') print(pattern.match('123, 123')) print(pattern.search('234, 234')) print(pattern.findall('345, 345')) # 方法2 print(re.match(r'd+', '123, 123')) print(re.search(r'd+', '234, 234')) print(re.findall(r'd+', '345, 345'))
在上述例子中,我们发现他们共用了同一个正则表达式,表明上看好像没发现什么问题,但是当我们结合正则表达式的匹配过程进行分析时,就会发现这两种调用方式的效率是不一样的。
使用正则表达式进行匹配的流程是先对正则表达式进行编译,然后得到一个对象,再使用该对象对需要匹配的文本进行匹配。这时我们就发现方式2会对正则表达式进行了多次编译,这样效率不就降低了。
所以我们可以得到如下结论:
如果一个正则表达式要用多次,那么出于效率考虑,我们可以预先编译正则表达式,然后调用的一系列函数时复用。如果直接使用re.match、re.search等函数,则需要每一次都对正则表达式进行编译,效率就会降低。因此在这种情况下推荐使用第一种方式。
三、元字符和通用字符
首先,认识常用的元字符:
.
匹配除 “ ” 和 “ ” 之外的任何单个字符。^
匹配字符串开始位置$
匹配字符串中结束的位置*
前面的原子重复 0 次、1 次、多次,相当于{0,}?
前面的原子重复 0 次或者 1 次,相当于{0,1}+
前面的原子重复 1 次或多次,相当于{1,}{n}
前面的原子出现了 n 次
{n,}
前面的原子至少出现 n 次{n,m}
前面的原子出现次数介于 n-m 之间( )
分组,输出需要的部分
再认识常用的通用字符:
s
匹配空白字符w
匹配任意字母/数字/下划线W
和小写 w 相反,匹配任意字母/数字/下划线以外的字符d
匹配十进制数字
D
匹配除了十进制数以外的值[0-9]
匹配一个 0~9 之间的数字[a-z]
匹配小写英文字母[A-Z]
匹配大写英文字母
四、贪婪与非贪婪匹配
正则表达式匹配时默认的是贪婪匹配,也就是会尽可能多的匹配更多字符。如果想使用非贪婪匹配,可以在正则表达式中加上 ?。
语法
说明
重复0或1次,但尽可能少重复
重复1次或更多次,但尽可能少重复
*
重复0次或更多次,但尽可能少重复
{n,m}
重复n到m次,但尽可能少重复
{n,}
重复n或更多次,但尽可能少重复
{n}
重复n次,但尽可能少重复
例子:
import re content = 'a<exp>hello world</exp>b<exp>ni hao</exp>c' pattern1 = re.compile(r'<exp>.*</exp>') s1 = pattern1.findall(content) # 贪婪匹配 会在匹配到第一个</exp>时继续向右匹配,查找更长的匹配子串 print(s1) # ['<exp>hello world</exp>b<exp>ni hao</exp>'] pattern2 = re.compile(r'<exp>.*?</exp>') # 非贪婪匹配 s2 = pattern2.findall(content) print(s2) # ['<exp>hello world</exp>', '<exp>ni hao</exp>']
五、分组
如果你想要提取子串或是想要重复提取多个字符,那么你可以选择用定义分组的形式。用()就可以表示要提取的分组(group),接下来用几个实例来理解一下分组的使用方式:
例子1:
import re m = re.match(r'(d{4})-(d{3,8})$', '0528-86889099') print(m.group()) # 0528-86889099 print(m.group(1)) # 0528 print(m.group(2)) # 86889099 print(m.groups()) # ('0528', '86889099')
正则表达式 (d{4})-(d{3, 8})$
表示匹配两个分组,第一个分组 (d{4})
是一个有4个数字的子串,第二个分组 (d{3,8})
表示匹配一个数字子串,子串长度为3到8之间。
例子2:
import re s = 'ab123.456.78.90c' m = re.search(r'(d{1,3}.){3}d{1,3}', s) print(m.group()) # 123.456.78.90 print(m.group(1)) # 78. print(m.groups()) # ('78.',)
正则表达式 (d{1,3}.){3}d{1,3} 的匹配过程分为两个部分,(d{1,3}.){3} 表示匹配一个长度为1到3之间的数字子串加上一个英文句号的字符串,重复匹配 3 次该字符串,d{1,3} 表示匹配一个1到3位的数字子串,所以最后得到的结果是 123.456.78.90
例子3:
import re line = 'Cats are smarter than dogs' match_obj = re.match(r'(.*) are (.*?) .*', line, re.M | re.I) if match_obj: print(match_obj.group()) # Cats are smarter than dogs print(match_obj.group(1)) # Cats print(match_obj.group(2)) # smarter else: print('no match')
(.*)
第一个分组,.*
代表匹配除换行符之外的所有字符。(.*?)
第二个匹配分组,.*?
后面加了个问号,代表非贪婪模式,只匹配符合条件的最少字符。后面的一个 .*
没有括号包围,所以不是分组,匹配效果和第一个一样,但是不计入匹配结果中。
group() 等同于group(0),表示匹配到的完整文本字符;
group(1) 得到第一组匹配结果,也就是 (.*)
匹配到的;
group(2) 得到第二组匹配结果,也就是 (.*?)
匹配到的;
因为只有匹配结果中只有两组,所以如果填 3 时会报错。
六、正则表达式修饰符
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过 |
来指定。
修饰符
含义
re.I
忽略大小写。
re.L
做本地化识别(locale-aware)匹配。对中文支持不好。
re.M
多行匹配,影响 ^ 和 $
re.S
单行。’.’ 匹配包括换行在内的所有字符
re.U
根据Unicode字符集解析字符。这个标志影响 w, W, , B.
re.X
忽略多余空白字符,让表达式更易阅读。
import re s = '123abCD45ABcd' print(re.findall(r'[a-z]+', s)) # ['ab', 'cd'] print(re.findall(r'[a-z]+', s, re.I)) # ['abCD', 'ABcd']
七、扩展
[一-龥] 中文表达式的范围 >>> import re >>> text = 'hello,马露露,五黑吗,go!' >>> re.findall(r'[一-龥]+', text) ['马露露', '五黑吗']