正则表达式(一)——基础之匹配字符,数量,边界

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 是描述字符串中的单词边界

接下来我们将进阶一步,看下正则表达式中的《匹配分组》,见正则表达式(二)——进阶之匹配分组

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值