1.概念
- 1.1 正则表达式概念
正则表达式,又称正规表达式,规则表达式,正规表示法等(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述,匹配一系列匹配某个句法规则的字符串。在很多文本编辑器中,通常被用来检索、替换那些符合某个模式(规则)的文本。
Regular Expression的“Regular“一般被译为“正则”,“正规”,“常规”。此处的”Regular“即是”规则“,”规律“之意。所以,Regular Expression即是”描述某种规则的表达式“之意。不用纠结”正则“之意。
- 1.2 re模块介绍
在python中需要通过正则表达式对字符串进行匹配时,需要用到一个模块,名字为re
- 1.re的使用过程
#导入re模块 import re #使用match方法进行匹配 result = re.match(正则表达式,要匹配的字符串) #如果上一步匹配到数据,可以使用group方法来提取数据 result.group()
re.match是用来进行正则匹配检查的方法,若字符串匹配正则表达式,则match方法返回匹配对象(Match Object),否则返回None(不是空字符串"")
匹配对象Match Object具有group方法,用来返回字符串的匹配部分。
- 2.re的简单操作示意
import re pattern = "test" #正则 match_str = "test code:hello,this is my test code" #要匹配的字符串 result = re.match(pattern,match_str) print(dir(result)) #打印匹配对象的所有方法,属性 '''['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']''' print(result) #打印匹配对象 # <_sre.SRE_Match object; span=(0, 4), match='test'> print(result.group()) #打印匹配内容,group()方法用来返回字符串的匹配部分 # test
如上面代码所示,Match Object有许多方法和属性,其中包括group()方法,用于返回字符串的匹配部分,且re匹配默认从左向右匹配。如果匹配,就不会继续查找了。
上述代码可以通过简单的字符串函数仅能实现,match_str.startswith(pattern),不具备任何通用性,未体现正则的意义,接下来就来真正了解正则的强大功能吧。
2 .学习正则语法
- 2.1 单字符匹配(表示字符)
字符 | 功能 |
. | 匹配任意1个字符(除了\n) |
[] | 匹配[]中列举的字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即空格,tab键 ,\n |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z A-Z 0-9 _ |
\W | 匹配非单词字符 |
- 代码示例
1) . 单字符
>>> re.match('..','a') #两个点,表示两个字符,'a'一个字符,未匹配到 None >>> re.match('...','abcd') #三个点,三个字符,匹配到'abc' <_sre.SRE_Match object; span=(0, 3), match='abc'> >>> re.match('.','\n') # . 无法匹配 '\n' None
2) \d \D 单字符
>>> re.match('\d','1a3') #一个数字字符,从左到右匹配到1 <_sre.SRE_Match object; span=(0, 1), match='1'> >>> re.match('\d*2','1a3') #两个连续数字字符,从左到右未匹配到 None >>> re.match('\d\d','123') <_sre.SRE_Match object; span=(0, 2), match='12'> #两个连续数字字符,从左到右匹配到 >>> re.match('\d*2','123') # \d\d == \d*2 <_sre.SRE_Match object; span=(0, 2), match='12'> >>> re.match('\d\D','1a3') #连续的,一个数字字符,一个非数字,匹配到 <_sre.SRE_Match object; span=(0, 2), match='1a'> >>> re.match('\D','a') #非数字字符,匹配到 <_sre.SRE_Match object; span=(0, 1), match='a'>
3)\s \S 单字符
>>> re.match('\s',' a') <_sre.SRE_Match object; span=(0, 1), match=' '> >>> re.match('\s','\ta') <_sre.SRE_Match object; span=(0, 1), match='\t'> >>> re.match('\s','\na') <_sre.SRE_Match object; span=(0, 1), match='\n'> >>> re.match('\S',' a') None
4)\w \W 单字符
>>> re.match('\w\w','_a') <_sre.SRE_Match object; span=(0, 2), match='_a'> >>> re.match('\w\W','_a') None
- Question one?
那么问题来了,比如在某个市内,手机号只能是1开头的,第二位数字只能是0-3,那么\d已经不能精确限制了,这时可使用[]
5) []
>>> re.match('1[0-3]','137') #第一位为1,第二位为0-3 <_sre.SRE_Match object; span=(0, 2), match='13'> >>> re.match('1[^0-3]','137') #^表示[]内取反 None >>> re.match('1[0-3a-z]','1h7') #第二位为0-3 或者 a-z <_sre.SRE_Match object; span=(0, 2), match='1h'> >>> re.match('1[^0-3a-z]','187') <_sre.SRE_Match object; span=(0, 2), match='18'>
注意 ^ 的用法,表示取反的意思,因此:
\d == [0-9]
\D == [^0-9]
\w == [a-zA-Z0-9_]
\W == [^a-zA-Z0-9_]
- Question two?
还是手机号校验问题,手机号有11位,那么就需要像这样re.match('1[0-3]\d\d\d\d\d\d\d\d\d\','13758265698'),写好多个\d用来匹配么,如果有一种方法可以表示数量的话,那不就perfect了么?答案当然是:of course,继续往下看吧......
- 2.2 多字符匹配(表示数量)
匹配多个字符的相关格式
字符 | 功能 |
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少1次 |
? | 匹配前一个字符出现1次或者0次,即至多1次 |
{m} | 匹配前一个字符出现m次 |
{m,} | 匹配前一个字符至少出现m次 |
{m,n} | 匹配前一个字符出现从m到n次 |
- 代码示例
1) * 可有可无
>>> re.match('\d*','') # * 表示可不出现数字,或任意次, ''为空,匹配不出现数字 <_sre.SRE_Match object; span=(0, 0), match=''> >>> re.match('\d*','abc') # 'abc'没有数字,匹配不出现数字,返回的'' 可看做'abc' == '''abc' <_sre.SRE_Match object; span=(0, 0), match=''> >>> re.match('\d*','13855824563') #匹配出现任意次数字 <_sre.SRE_Match object; span=(0, 11), match='13855824563'>
2)+ 至少一次
>>> re.match('\d+','123') #至少1次数字, '123'有3个数字,匹配 <_sre.SRE_Match object; span=(0, 3), match='123'> >>> re.match('\d+','abc') 'abc'没有1个数字,不匹配 None >>> re.match('\d+','123abc') '123abc'有3个数字,匹配 <_sre.SRE_Match object; span=(0, 3), match='123'>
3) ? 至多1次
>>> re.match('\d?','abc') #'abc'未出现数字,匹配 <_sre.SRE_Match object; span=(0, 0), match=''> >>> re.match('\d?','1abc') #出现1次数字,匹配 <_sre.SRE_Match object; span=(0, 1), match='1'> >>> re.match('\d?','12abc') #出现1次数字,匹配。 注意,这里\d值描述了一位信息,2是没有限定的 <_sre.SRE_Match object; span=(0, 1), match='1'> >>> re.match('\d?[a-z]','12abc') #限定第二位为[a-z],不匹配 None >>> re.match('\d*[a-z]','123a4abc') #出现任意次数字后,限定其后为[a-z] <_sre.SRE_Match object; span=(0, 4), match='123a'> >>> re.match('\d+[a-z]','1234abc') #至少出现1次数字后,其后为[a-z] <_sre.SRE_Match object; span=(0, 5), match='1234a'>
4) {m} {m,}
>>> re.match('\d{4}[a-z]','1234abc') #4个数字后,跟上[a-z],匹配 <_sre.SRE_Match object; span=(0, 5), match='1234a'> >>> re.match('\d{5}[a-z]','1234abc') #5个数字后,跟上[a-z],不匹配 None >>> re.match('\d{3}[a-z]','1234abc') #3个数字后,跟上[a-z],不匹配 None # {m,} >>> re.match('\d{3,}[a-z]','1234abc') #3个数字以上 <_sre.SRE_Match object; span=(0, 5), match='1234a'>
那么,{}就可以表示数量符了:
{1,} == +
{0,} == *
{0,1} == ?
那么手机号就可以表示为:re.match('1[3-8]\d{9}','18155825579')
>>> re.match('1[3-8]\d{9}','18155825579') <_sre.SRE_Match object; span=(0, 11), match='18155825579'> >>> re.match('1[3-8]\d{9}','18155825579abcd') <_sre.SRE_Match object; span=(0, 11), match='18155825579'>
- Question three?
re.match('1[3-8]\d{9}','18155825579abcd')
<_sre.SRE_Match object; span=(0, 11), match='18155825579'>也是显示匹配的,后面的'abcd'还未排除,我们还未解决边界问题,继续来看吧......
插个小知识点:正则表达式\ 和 原生字符串r
r表示原生字符,也就是说不转义字符不需要再次被转义。比如你要表示'\n',可以这样r'\n',但是你不用原生字符,而用字符串必须手动转义'\\n'。区别看如下代码:
>>> s = r'\nabc' #原生字符 >>> s '\\nabc' #自动帮你添加转义 >>> print(s) \nabc >>> s = '\nabc' #非原生字符,未转义 >>> s '\nabc' >>> print(s) abc >>> s = '\\nabc' #非原生字符,转义,效果与r一样 >>> s '\\nabc' >>> print(s) \nabc
那么在re中看下区别吧:
>>> s = '\\nabc' >>> r'\\nabc' #r 会自动帮你转义 '\\\\nabc' >>> re.match('\\nabc',s) None >>> re.match('\\\\nabc',s) #非原生字符,手动转义 <_sre.SRE_Match object; span=(0, 5), match='\\nabc'> >>> re.match(r'\\nabc',s) #原生字符 <_sre.SRE_Match object; span=(0, 5), match='\\nabc'>
因此在经常正则匹配时,通常加上r,这样我们就不必担心表达式有\而去手动加上转义符\
- 2.3 表示边界
字符 | 功能 |
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词边界 |
- 代码示例
1) $ 匹配结尾
继续以之前的手机号末尾多出来的‘abcd’问题为例
>>> re.match('1[3-8]\d{9}$','18155825579abcd') None >>> re.match('1[3-8]\d{9}$','18155825579') <_sre.SRE_Match object; span=(0, 11), match='18155825579'>
这样,加了个结尾$就能限定边界,一共11位数字了
2)^ 匹配开头
>>> re.match('^1[3-8]\d{9}$','18155825579') #以1开头 <_sre.SRE_Match object; span=(0, 11), match='18155825579'> >>> re.match('^1[3-8]\d{9}$','28155825579') None >>> re.match('^[12][3-8]\d{9}$','28155825579') #以1或2开头 <_sre.SRE_Match object; span=(0, 11), match='28155825579'>
这个效果在match中不是很明显,原因match就是从左到右开始匹配的。
3)\b 单词边界
>>> re.match(r'^\w+ve','hover') <_sre.SRE_Match object; span=(0, 4), match='hove'> >>> re.match(r'^\w+ve\b','hover') #以ve结尾,不匹配 None '''高级点,注意\b不代表字符,也不代表空格,加空格\s''' >>> re.match(r'^\w+\bve\b','hover') #以至少1个字符为单词,再以ve为单词,明显不符合实际:单词划分以空格为基础,既然没有空格,必然没有两个单词 None >>> re.match(r'^\w+\b\sve\b','hover ve') #加上空格,成为两个单词,空格也要显式写上去\s <_sre.SRE_Match object; span=(0, 8), match='hover ve'> >>> re.match(r'^\w+\bve\b','ho ve r') None >>> re.match(r'^\w+\s\bve\b','ho ve r') #同理,加上空格 <_sre.SRE_Match object; span=(0, 5), match='ho ve'> >>> re.match(r'^\w+\bve\b','hove r') None >>> re.match(r'^\w+ve\b','hove r') #匹配至少1个字符,以ve结尾单词 <_sre.SRE_Match object; span=(0, 4), match='hove'> '''看看这个区别''' >>> re.match(r'^.+\b\sve\b','ho ve r') #表示任意字符为边界,多了个空格,此时解析为:ho为1个单词,ve为1个 <_sre.SRE_Match object; span=(0, 5), match='ho ve'> >>> re.match(r'^.+\bve\b','ho ve r') #表示任意字符为边界,没有空格,此时解析为:ho 为1个单词(有个空格),ve为1个 <_sre.SRE_Match object; span=(0, 5), match='ho ve'>
4)\B 非单词边界
>>> re.match(r'^.+ve\B','ho ve r') None >>> re.match(r'^.+ve\B','ho ver') #ve不是单词边界,匹配上 <_sre.SRE_Match object; span=(0, 5), match='ho ve'> >>> re.match(r'^.+\Bve\B','ho ver') #ho是单词边界,未匹配上 None >>> re.match(r'^.+\Bve\B','hover') <_sre.SRE_Match object; span=(0, 4), match='hove'>
总结下:
^ 和 $ 是描述整个字符串的边界
\b 和 \B 是描述字符串中的单词边界
接下来我们将进阶一步,看下正则表达式中的《匹配分组》,见正则表达式(二)——进阶之匹配分组