本文为原创文章,未经本人允许,禁止转载。转载请注明出处。
1.正则表达式
re
模块使Python语言拥有全部的正则表达式功能。
1.1.re.search()
re.search
扫描整个字符串并返回第一个成功的匹配。
1
2
3
4
5
6
import re
a = '23123'
re.search('1', a)#输出为:<re.Match object; span=(2, 3), match='1'>
re.search('23', a)#输出为:<re.Match object; span=(0, 2), match='23'>
re.search('1', a).span()#输出为:(2, 3)
re.search('23', a).span()#输出为:(0, 2)
如果想匹配多个字符中的任意一个,可使用[]
。返回[]
中任意一个字符的第一个成功匹配:
1
2
re.search('[0123]', a)#输出为:<re.Match object; span=(0, 1), match='2'>
re.search('[13]', a)#输出为:<re.Match object; span=(1, 2), match='3'>
如果匹配失败,则返回None
,例如:
1
re.search('4', a)#输出为:None
此外,re.search('[0123456789]', a)
可简写为re.search('[0-9]', a)
或者re.search('\d', a)
(\d
表示任意数字的匹配)。
同样的,除了对数字的查找,也可以对字母进行查找:
1
2
3
4
5
6
7
a = 'cdbapa'
re.search('a', a)#输出为:<re.Match object; span=(3, 4), match='a'>
re.search('[abcdefghijklmnopqrstuvwxyz]', a)#输出为:<re.Match object; span=(0, 1), match='c'>
re.search('[a-z]', a)#输出为:<re.Match object; span=(0, 1), match='c'>
b = 'A'
re.search('[a-z]', b)#输出为:None
re.search('[a-zA-Z]', b)#输出为:<re.Match object; span=(0, 1), match='A'>
re.search('[a-zA-Z0-9]', b)
可简写为:re.search('\w', b)
:
1
2
3
b = 'A4'
re.search('[a-zA-Z0-9]', b)#输出为:<re.Match object; span=(0, 1), match='A'>
re.search('\w', b)#输出为:<re.Match object; span=(0, 1), match='A'>
使用{n}
可以限定匹配n个字符:
1
2
3
4
5
a = 'abc'
re.search('\w'), a)#输出为:<re.Match object; span=(0, 1), match='a'>
re.search('\w{2}'), a)#输出为:<re.Match object; span=(0, 2), match='ab'>
re.search('\w{3}'), a)#输出为:<re.Match object; span=(0, 3), match='abc'>
re.search('[acabdf]{2}', a)#输出为:<re.Match object; span=(0, 2), match='ab'>
更进一步的,使用{a,b}
表示在满足最小匹配a个字符的前提下,返回其不超过b的最大匹配:
1
2
3
4
5
a = 'abc'
b = 'abcdefg'
re.search('\w{4,15}', b)#输出为:<re.Match object; span=(0, 7), match='abcdefg'>
re.search('\w{4,6}', b)#输出为:<re.Match object; span=(0, 6), match='abcdef'>
re.search('\w{4,15}', a)#输出为:None
当只限制最小匹配为1个及以上时,可以使用符号+
,即相当于{1,inf}
。例如写成如下形式:
1
2
3
4
b = 'abcdefg'
re.search('\w+', b)#输出为:<re.Match object; span=(0, 7), match='abcdefg'>
b = 'a'
re.search('\w+', b)#输出为:<re.Match object; span=(0, 1), match='a'>
类似的,使用符号*
相当于{0,inf}
:
1
2
3
b = ''
re.search('\w+', b)#输出为:None
re.search('\w*', b)#输出为:<re.Match object; span=(0, 0), match=''>
举个实际应用的例子,查找电话号码:
1
2
a = 'my phone is 134-1234-1234'
re.search('\d{3}-\d{4}-\d{4}', a)#输出为:<re.Match object; span=(12, 25), match='134-1234-1234'>
如果电话簿中同时还存在另外一种电话的记录形式,即没有符号-
,例如:'my phone is 13412341234'
。此时便需要我们可以同时处理以上两种号码的记录方式:
1
2
3
4
5
6
7
8
a = 'my phone is 134-1234-1234'
b = 'my phone is 13412341234'
#方法一:
re.search('\d{3}-{0,1}\d{4}-{0,1}\d{4}', a)#输出为:<re.Match object; span=(12, 25), match='134-1234-1234'>
re.search('\d{3}-{0,1}\d{4}-{0,1}\d{4}', b)#输出为:<re.Match object; span=(12, 23), match='13412341234'>
#方法二:
re.search('\d{3}-?\d{4}-?\d{4}', a)#输出为:<re.Match object; span=(12, 25), match='134-1234-1234'>
re.search('\d{3}-?\d{4}-?\d{4}', b)#输出为:<re.Match object; span=(12, 23), match='13412341234'>
?
即表示{0,1}
。
❗️汇总一下正则表达式中可能会用到的一些符号:
.
:比对除换行外的任意字符。^
:比对字符串开始。$
:比对字符串结尾。*
:比对0个或多个由前面正则表达式定义的片段,贪婪方式。+
:比对1个或多个由前面正则表达式定义的片段,贪婪方式。?
:比对0个或1个由前面正则表达式定义的片段,贪婪方式。*?
、+?
、??
:非贪婪版本的*
、+
和?
(尽可能少的比对)。[...]
:比对[]内字符集中的任意一个字符。(...)
:比对()内的表达式,也表示一个群组。(?P<id>...)
:类似(…),但该组同时得到一个id,可以在后面的模式中引用。
1.2.re.match()
re.match()
尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
⚠️re.match()
与re.search()
的区别:re.match()
只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search()
匹配整个字符串,直到找到一个匹配。
假如我们现在要使用re.match()
来匹配邮箱:
1
2
email = 'david@largidata.com'
re.match('\w+@\w+', email)#输出为:<re.Match object; span=(0, 15), match='david@largidata'>
如果我们想要将david
和largidata
两个信息单独提取出来,则我们需要使用()
对其进行分组并使用group()
或groups()
进行提取:
1
2
3
4
5
6
7
8
m = re.match('(\w+)@(\w+)', email)
print(m)#输出为:<re.Match object; span=(0, 15), match='david@largidata'>
print(m.group())#输出为:david@largidata
print(m.group(1))#输出为:david
print(m.group(2))#输出为:largidata
print(m.groups())#输出为:('david', 'largidata')
print(m.groups()[0])#输出为:david
print(m.groups()[1])#输出为:largidata
如果需要将.com也提取出来,则需要加上对句点.
的匹配,但是.
在正则表达式中已经有了特殊的含义,因此对句点.
的匹配可以使用\.
:
1
2
3
4
5
m = re.match('(\w+)@([a-z\.]+)', email)
print(m.groups())#输出为:('david', 'largidata.com')
#m可简写为:
m = re.match('(\w+)@(.+)', email)
print(m.groups())#输出为:('david', 'largidata.com')
再举一个数字匹配的例子:
1
2
3
digit = '1999.5'
m = re.match('(\d+)\.(\d+)', digit)
print(m.groups())#输出为:('1999', '5')
如果现在我们要提取名字中的first name和last name,根据以上学过的知识,我们可以这样实现:
1
2
3
4
name = 'David Chiu'
m = re.match('(\w+) (\w+)', name)
print(m.group(1))#输出为:David
print(m.group(2))#输出为:Chiu
此外,我们可以使用(?P<id>...)
为其添加标签:
1
2
3
4
name = 'David Chiu'
m = re.match('(?P<first_name>\w+) (?P<last_name>\w+)', name)
print(m.group('first_name'))#输出为:David
print(m.group('last_name'))#输出为:Chiu
^
和$
的使用:^
从第一个字符开始匹配;$
从倒数第一个字符开始匹配。
1
2
3
4
5
6
s = '123 abc'
re.search('^[0-9]', s)#输出为:<re.Match object; span=(0, 1), match='1'>
re.search('^[2-9]', s)#输出为:None
re.search('^[a-z]', s)#输出为:None
re.search('[0-9]$', s)#输出为:None
re.search('[a-z]$', s)#输出为:<re.Match object; span=(6, 7), match='c'>
2.在DataFrame上使用正则表达式
以以下数据为例(数据下载链接):
用正则表达式从“户型”中抽取“室”、“厅”、“厨”、“卫”:
1
df[['室', '厅', '厨', '卫']] = df['户型'].str.extract('(\d+)室(\d+)厅(\d+)厨(\d+)卫', expand=False)