正则表达式
字符串是编程时遇到最多的一种数据结构,对字符串的操作无所不在。比如判断一个字符串是否是合法的email地址,虽然编程可以取@前后的字符串,再分别判断是否是单词和域名,但这样做非常麻烦,而且代码很难复用。
正则表达式是一种用来匹配字符串的强有力工具。它的设计思想是一用一种描述性语言给字符串定义一个规则,凡是符合规则的字符串,我们就认为它匹配了,否则该字符串就是不合法的。
所以,我们判断一个字符串是否是合法email的方法:
1、创建一个匹配email的正则表达式。
2、用该正则表达式匹配用户的输入是否合法。
因为正则表达式也是用字符串表示的,所以,我们首先了解如何用字符表示字符。
在正则表达式中,如果直接给出字符,就是精确匹配。用\d匹配数字,用\w匹配一个字母或数字,所以:
'00\d' 可以匹配到'007',但是无法匹配到'00p'
'\w\w\d'可以匹配到'py3'
.可以匹配任意字符,所以:
'py.'可以匹配'pyc' 'pyo' 'py!'等等。
要匹配变长的字符,在正则表达式中,用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符。用{n-m}表示n-m个字符:
来看一个复杂的例子:
\d{3}\s+\d{3-8}
\s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少一个空格,例如匹配' ',' '等。
\d{3-8}表示3-8个数字,例如:'12345678'
综合起来,上面的正则表达式可以任意匹配以任意空格隔开的电话号码。
如果要匹配'010-345678'这样的电话号码呢?因为'-'是特殊字符,在正则表达式中需要使用'\'转义,所以: \d{3}\-\d{3-8}
但是仍然无法匹配'010 - 3456'这样的电话号码,因为带有空格,所以需要更复杂的正则表达式。
进阶
如果要精确的匹配,可以用[]表示范围,比如:
[0-9a-zA-Z\_]可以匹配一个数字字母或下划线。
[0-9a-zA-Z\_]+可以匹配至少由一个数字、字母、或下划线组成的字符串。比如'a100','0_z','py300'
[a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下划线开头,后接任意个由字母数字或下划线组成的字符串,也就是python的合法变量。
[a-zA-Z\_][0-9a-zA-Z\_]{0,19}更精确的限制了变量的长度是1-20个字符(前面一个字符+后面最多19个字符)
A|B可以匹配A或B,所以[P|python]可以匹配'Python'或者'python'
^表示行的开头,^\d表示必须以数字开头。
$表示行的结束,\d$表示必须以数字结束。
你可能注意到了,py也可以匹配‘python’,但是加上^py$,这样就只能匹配'py'了。
re模块
有了准备知识,我们就可以在Python中使用正则表达式了。Python提供re模块,包含所有正则表达式功能。由于Python本省的字符串也使用\转义,以下要特别注意:
s ='abc\\-001' #Python的字符串
转义之后:'abc\-001'
因此,我们强烈推荐使用python的r前缀,就不用考虑转义的问题了:
s =r'abc\-001'
转义后:'abc\-001'
先看看如何判断正则表达式是否匹配:
>>>import re
>>>re.match(r'^\d{3}\-\d{3,8}$','010-12345')
<_sre.SRE_Match object; span=(0,9),match='010-12345'>
>>>re.match(r'^\d{3}\-\d{3,'010 12345')
>>>
可以看出,match()方法如果匹配,则返回一个Match对象,否则返回None。常见的判断方法:
test ='用户输入的字符串'
if re.match(r'正则表达式',test):
print('ok')
else:
print('Failed')
切分字符串
用正则表达式切分字符串比使用固定的字符串更灵活,请看正常的切分代码:
>>>'ab c'.split(' ')
['a','b','','c']
无法识别连续的空格,用正则表达式试试:
>>>re.split(r'\s+','ab c')
['a','c']
无论多少个空格都可以正常分割,加入,试试:
>>>re.split(r'[\s\,]+','a,bc,d')
['a','c','d']
再加入;试试:
>>>re.mathch(r'[\s\,\;]+',;b;c ;;d')
['a','d']
如果用户输入了一组标签,下次记得使用正则表达式把不规范的输入转成正确的数组。
分组
除了简单的判断是否匹配之外,正则表达式还有提取字串的强大功能。用()表示的就是要提取的分组(Group)。比如:
^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和号码:
>>> m =re.match(r'^(\d{3})-(\d{3-8})$','010-12345')
>>>m
<_sre.SRE_Match object; span=(0,match='010-12345'>
>>>m.group(0)
'010-12345'
>>>m.group(1)
'010'
>>>m.group(2)
'12345'
如果正则表达式中定义了组,就可以在Match对象上使用group()方法提取出来。
注意到,group(0)是原始字符串,group(1)\group(2)...依次表示第一个、第二个字符串...
提取字符串非常有用,来看一个更凶残的例子:
>>>t ='19:05:30'
>>>m =re.match(r'^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$',t)
>>>m.groups()
('19','05','30')
这个正则表达式可以直接识别合法时间但有时候使用正则表达式也无法做到完全验证,如识别日期:
'^(0[1-9]|1[0-2]|[0-9])-(0[1-9]|1[0-9]|2[0-9]|3[0-1]|[0-9])$'
对于日期:'2-30','4-31'这样的非法日期还是识别不了,或者写出来非常困难,这样就需要程序配合识别了。
贪婪匹配
最后需要指出的是正则表达式默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下:
>>>re.match(r'^(\d+)(0*)$','102300').groups()
('102300','')
由于\d+采用贪婪匹配,把后面的0全部匹配掉了,所以0*只能匹配空字符串了。
必须让\d+采用非贪婪匹配,才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪方式匹配:
>>>re.match(r'^(\d+?)(0*)$','12300').groups()
('123','00')
编译
当我们在Python中使用正则表达式时,re模块内部会做两件事:
1、编译正则表达式,如果正则表达式本身不合法,会报错
2、用编译后的正则表达式取匹配字符串
如果一个正则表达式要使用几千次,出于效率考虑,我们可以预编译正则表达式,接下来重复使用就不需要编译这个步骤了,直接匹配:
>>>import re
>>>re_telephone =re.compile(r'^(\d{3})-(\d{3,8})$')
>>>re_telephone.match('010-12345').groups()
('010','12345')
编译后生成Regular Expression对象,由于该对象自己包含了正则表达式,所以调用时不用给出正则字符串。
小结:
正则表达式非常强大,要在一节内讲完是不可能的,如果你经常用到正则表达式,你可能需要一本正则表达式的参考书。
原文链接:https://www.f2er.com/regex/359571.html