1.正则表达式简介
正则表达式用于方便的检查一个字符串是否与某种模式匹配。
比如从如下文本里面获取所有职位的薪资。
用学过的python基础对字符串进行处理,也可以用下面代码解决。
content = '''
Python3 高级开发工程师 上海互教教育科技有限公司上海-浦东新区2万/月02-18满员
测试开发工程师(C++/python) 上海墨鹍数码科技有限公司上海-浦东新区2.5万/每月02-18未满员
Python3 开发工程师 上海德拓信息技术股份有限公司上海-徐汇区1.3万/每月02-18剩余11人
测试开发工程师(Python) 赫里普(上海)信息科技有限公司上海-浦东新区1.1万/每月02-18剩余5人
Python高级开发工程师 上海行动教育科技股份有限公司上海-闵行区2.8万/月02-18剩余255人
python开发工程师 上海优似腾软件开发有限公司上海-浦东新区2.5万/每月02-18满员
'''
lines = content.splitlines()
for line in lines:
pos2 = line.find('万/月')
if pos2 < 0:
pos2 = line.find('万/每月')
if pos2 < 0:
continue
idx = pos2-1
while line[idx].isdigit() or line[idx]=='.':
idx -= 1
pos1 = idx + 1
print(line[pos1:pos2])
但是相对于用正则表达式来说,就略有繁琐。
使用正则表达式仅需三行代码即可解决这个问题。
import re
for one in re.findall(r'([\d.]+)万/每{0,1}月', content):
print(one)
([\d.]+)万/每{0,1}月 ,就是正则表达式字符串,指定了 搜索子串的特征。
findall 函数在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。
2. 点
.表示要匹配除了换行符之外的任何单个字符
比如在如下文本中找出所有颜色。
content = '''苹果是绿色的
橙子是橙色的
香蕉是黄色的
乌鸦是黑色的'''
import re
a = re.compile(r'.色')#前面加r表示不进行python语法的字符串转义
for i in a.findall(content):
print(i)
运行结果:
绿色
橙色
黄色
黑色
其中 点 代表了任意的一个字符, 注意是一个字符。
若将.色改为…色,(色前面加上两个点),运行结果为:
是绿色
是橙色
是黄色
是黑色
补充:compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
语法格式为:re.compile(pattern[, flags])
pattern : 一个字符串形式的正则表达式
flags 可选,表示匹配模式,比如忽略大小写,多行模式等
3. 星号
星号表示匹配前面的子表达式任意次,包括0次。
比如,从文本中,选择每行逗号后面的字符串内容,包括逗号本身。
content = '''苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,'''
import re
a = re.compile(r',.*')#前面加r表示不进行python语法的字符串转义
for i in a.findall(content):
print(i)
运行结果是:
,是绿色的
,是橙色的
,是黄色的
,是黑色的
,
*紧跟在 . 后面,表示任意字符可以出现任意次, 所以整个表达式的意思就是在逗号后面的所有字符,包括逗号
当然,*前面可以是任意字符,可以匹配任意字符任意多次。
如下所示:
content = '''苹果,是绿色的
橙子,是橙色色色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,'''
import re
a = re.compile(r'橙色*')#前面加r表示不进行python语法的字符串转义
for i in a.findall(content):
print(i)
该段代码的运行结果是:
橙
橙色色色
第一个结果匹配的是橙子的橙,这里匹配了零次色
第二个结果匹配的是橙色色色,这里匹配了三次色
4. 加号
在正则表达式中,加号表示匹配前面的子表达式一次或多次,不包括0次。
比如选择每行逗号后面的字符串内容,包括逗号本身,如果逗号后面没有内容,则不选择。
content = '''苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,'''
import re
a = re.compile(r',.+')#前面加r表示不进行python语法的字符串转义
for i in a.findall(content):
print(i)
运行结果是:
,是绿色的
,是橙色的
,是黄色的
,是黑色的
最后一行逗号后面没有内容,没有进行选择。
5. 问号
在正则表达式中,? 表示匹配前面的子表达式0次或1次。
如从文本中,选择每行逗号后面的1个字符,也包括逗号本身。
content = '''苹果,是绿色的
橙子,是橙色的
香蕉,是黄色的
乌鸦,是黑色的
猴子,'''
import re
a = re.compile(r',.?')#前面加r表示不进行python语法的字符串转义
for i in a.findall(content):
print(i)
输出结果是:
,是
,是
,是
,是
,
前四行中,问号表示匹配一次,最后一行,问号表示匹配零次
6. 花括号
在正则表达式中,花括号表示前面的字符匹配指定的次数 。
content = '''苹果,是绿色色色的
橙子,是橙色的
'''
import re
a = re.compile(r'色{3,4}')#前面加r表示不进行python语法的字符串转义
for i in a.findall(content):
print(i)
运行结果是:色色色
表达式色{3} 就表示匹配连续的色字 3次
表达式色{3,4} 就表示匹配连续的色字至少3次,至多 4 次
7. 贪婪模式和非贪婪模式
贪婪模式和贪婪模式
如果要提取如下字符串中的HTML标签,
source = '<html><head><title>Title</title>'
希望得到如下的列表:
['<html>', '<head>', '<title>', '</title>']
我们可以使用正则表达式<.*>
代码如下:
source = '<html><head><title>Title</title>'
import re
a = re.compile(r'<.*>')
print(a.findall(source))
但是运行之后得到的结果是:
['<html><head><title>Title</title>']
并不是我们想要的结果。
这是因为在正则表达式中,星号、加号和问号都是贪婪的,他们在使用时会尽可能多的匹配内容,上面代码中的星号一直匹配到了字符串的最后。
解决这个问题需要改为非贪婪模式,也就是在星号后面加上 ? ,变成 <.*?>
代码改为:
source = '<html><head><title>Title</title>'
import re
a = re.compile(r'<.*?>')
print(a.findall(source))
这样就可以得到我们需要的结果。
8. 对元字符的转义
如果我们要搜索的内容本身就包含元字符,就可以使用反斜杠进行转义。
在下面的文本中搜索所有点前面的字符串时,也包含点本身,正则表达式不能写成 .*.
苹果.是绿色的
橙子.是橙色的
香蕉.是黄色的
因为点是一个元字符,直接出现在正则表达式中,表示匹配任意的单个字符,不能表示 . 这个字符本身的意思了。
这时可以使用反斜杠进行转义,将表达式写为 .*.
代码如下:
content = '''苹果.是绿色的
橙子.是橙色的
香蕉.是黄色的'''
import re
a = re.compile(r'.*\.')#前面加r表示不进行python语法的字符串转义
for one in a.findall(content):
print(one)
运行结果为:
苹果.
橙子.
香蕉.
9. 方括号
正则表达式中方括号表示要匹配指定的几个字符之一 。
比如:
[abc] 可以匹配 a, b, 或者 c 里面的任意一个字符,等价于 [a-c] 。
[a-c] 中间的 - 表示一个范围从a 到 c。
[a-z]可以匹配所有的小写字母
一些元字符在方括号内就变得和普通字符一样了。
比如:
[akm.] 匹配 a k m . 里面任意一个字符,其中.在括号里面不在表示匹配任意字符,而是表示匹配 . 这个字符
如果在方括号中使用 ^ , 表示非方括号里面的字符集合。
比如:\d 匹配0-9之间任意一个数字字符,[^\d] 表示选择非数字的字符。
content = 'a1b2c3d4e5'
import re
a = re.compile(r'[^\d]')#前面加r表示不进行python语法的字符串转义
for one in a.findall(content):
print(one)
运行结果为:
a
b
c
d
e
又如提取有效的手机号。
1表示第一个数字为1,[36]表示第二个数字为3或6,\d{9}表示匹配9个0-9之间的数字
content = '''
王一,13512345678,89
徐二,1b098766456,24
李三,13212394765,33
'''
import re
a = re.compile(r'1[36]\d{9}')#前面加r表示不进行python语法的字符串转义
for one in a.findall(content):
print(one)
运行结果为:
13512345678
13212394765
10. 单行、多行模式
^ 表示匹配文本的开头位置。
正则表达式可以设定单行模式和多行模式
如果是单行模式 ,表示匹配整个文本的开头位置。
如果是多行模式 ,表示匹配文本每行的开头位置。
比如提取文本中水果的编号:
content = '''001-苹果价格-60
002-橙子价格-70
003-香蕉价格-80'''
import re
p = re.compile(r'^\d+', re.M)#前面加r表示不进行python语法的字符串转义
for one in p.findall(content):
print(one)
运行结果为:
001
002
003
^\d+表示匹配开头位置的一个或多个数字,re.M表示多行模式,若去掉re.M则为单行模式,输出结果为001。
因为在单行模式下,^只会匹配整个文本的开头位置。
$ 表示匹配文本的结尾位置。
如果是单行模式 ,表示匹配整个文本的结尾位置。
如果是多行模式 ,表示匹配文本每行的结尾位置。
比如提取文本末尾的水果价格:
content = '''001-苹果价格-60
002-橙子价格-70
003-香蕉价格-80'''
import re
p = re.compile(r'\d+$',re.MULTILINE)#前面加r表示不进行python语法的字符串转义
for one in p.findall(content):
print(one)
运行结果为:
60
70
80
re.MULTILINE指明了使用多行模式,若去掉re.MULTILINE,则为单行模式,运行结果为80。因为单行模式下,$ 只会匹配整个文本的结束位置。
11. 括号
括号为正则表达式的组选择。
组就是把正则表达式匹配的内容里面其中的某些部分标记为某个组。
我们可以在正则表达式中标记多个组
为什么要有组的概念呢?因为我们往往需要提取已经匹配的内容里面的某些部分的信息。
从下面文本中,选择每行逗号前面的字符串,也包括逗号本身 。
苹果,苹果是绿色的
橙子,橙子是橙色的
香蕉,香蕉是黄色的
就可以这样写正则表达式 ^.*,
但是,如果我们要求不要包括逗号呢?
当然不能直接这样写 ^.*
因为最后的逗号是特征所在, 如果去掉它,就没法找逗号前面的了。
但是把逗号放在正则表达式中,又会包含逗号。
解决问题的方法就是使用组选择符 :括号。
正则表达式可以写为 ^(.*),
比如:
content = '''苹果,苹果是绿色的
橙子,橙子是橙色的
香蕉,香蕉是黄色的'''
import re
p = re.compile(r'^(.*),', re.MULTILINE)#前面加r表示不进行python语法的字符串转义
for one in p.findall(content):
print(one)
运行结果为:
苹果
橙子
香蕉
当然,分组还可以进行多次。
比如,从下面的文本中选择姓名和手机号
张三,手机号码15945678901
李四,手机号码13945677701
王二,手机号码13845666901
可以使用正则表达式^(.+),.+(\d{11})
第一个分组表示获取逗号前面的姓名,第二个分组表示获取后十一位手机号码
12. 切割字符串
正则表达式中的split方法可以用于对字符串进行灵活的切割。
比如切割如下字符串:
names = '关羽; 张飞, 赵云,马超, 黄忠 李逵'
我们可以发现每两个名字之间,有的是分号隔开,有的是逗号隔开,有的是空格隔开, 而且分割符号周围还有不定数量的空格。
这时可以使用正则表达式[;,\s]\s*
分割符为分号、逗号、空格里面的任意一种均可。\s*表示任意数量的空格。
import re
names = '关羽; 张飞, 赵云, 马超, 黄忠 李逵'
namelist = re.split(r'[;,\s]\s*', names)#前面加r表示不进行python语法的字符串转义
print(namelist)
运行结果为:[‘关羽’, ‘张飞’, ‘赵云’, ‘马超’, ‘黄忠’, ‘李逵’]
13. 字符串替换
正则表达式中的split方法可以用于对字符串进行灵活的切割。
比如切割如下字符串:
names = '关羽; 张飞, 赵云,马超, 黄忠 李逵'
我们可以发现每两个名字之间,有的是分号隔开,有的是逗号隔开,有的是空格隔开, 而且分割符号周围还有不定数量的空格。
这时可以使用正则表达式[;,\s]\s*
分割符为分号、逗号、空格里面的任意一种均可。\s*表示任意数量的空格。
import re
names = '关羽; 张飞, 赵云, 马超, 黄忠 李逵'
namelist = re.split(r'[;,\s]\s*', names)#前面加r表示不进行python语法的字符串转义
print(namelist)
运行结果为:[‘关羽’, ‘张飞’, ‘赵云’, ‘马超’, ‘黄忠’, ‘李逵’]