正则表达式

向我们平常见的那些注册页面啥的,都需要我们输入手机号码,你想我们的电话号码也是有限的吧。(手机号码一i共11位,并且只以13,14,15,17,18开头的数字这些特点)如果你的输入有误就会提示,那么现实中这个程序的话你觉得用while循环判断简单吗,那么我们看看实现结果

while True:
    phone_number=input('请输入你的电话号码:')
    if len(phone_number)==11 and phone_number.isdigit()\
        and (phone_number.startswith('13')\
        or phone_number.startswith('14') \
        or phone_number.startswith('15') \
        or phone_number.startswith('17') \
        or phone_number.startswith('18')):
        print('是合法的手机号码')
    else:
        print('不是合法的手机号码')

看到这个代码,虽说很容易理解,但是还有更简单的方法

import re
phone_number=input('请输入你的电话号码:')
if re.match('^(13|14|15|17|18)[0-9]{9}$',phone_number):
    '''^这个符号表示的是判断是不是以13|14|15|17|18开头的,
    [0-9]: []表示一个字符组,可以表示0-9的任意字符
    {9}:表示后面的数字重复九次
    $:表示结束符
    '''
    print('是合法的手机号码')
else:
    print('不是合法的手机号码')

那么什么是正则呢?

  首先你要知道的是,谈到正则,就只和字符串相关了。在线测试工具http://tool.chinaz.com/regex/

比如你要用‘1’去匹配‘1’,直接就可以匹配上。

字符组

字符组:【字符组】

在同一位置可能出现的各种字符组成了一个字符组,在正则表达式中用【】表示,字符分为很多类,比如数字,字母,标点等等。

假如你现在要求一个位置‘只能出现一个数字’,那么这个位置上的字符只能是0,1,2,3....9

 

 

 字符

 

 

 量词

 

 

 

.^$

 

 

 *+?{}

 

 

 注意:前面的*,+,?等都是贪婪匹配,也就是尽可能多的匹配,后面加?就变成了非贪婪匹配,也就是惰性匹配。

贪婪匹配

 

 

 几个常用的贪婪匹配

*?;重复任意次,但尽可能少重复
+?:重复一次或更多次,但尽可能少重复
??:重复0次或1次,但尽可能少重复
{n,m}:重复n到m次,但尽可能少重复
{n,}: 重复n次以上,但尽可能少重复

 

.*?的用法:

.是任意字符
*是取0到无限长度
?是非贪婪模式
和在一起就是取尽量少的任意字符,一般不会这么单独写,大多用在:
.*?x
意思就是取前面任意长度的字符,直到一个x出现

 

字符集:

 

 分组()与或 | 【^】

(1)^[1-9]\d{13,16}[0-9x]$     #^以数字0-9开始,
                                \d{13,16}重复13次到16次
                                $结束标志
上面的表达式可以匹配一个正确的身份证号码
 
(2)^[1-9]\d{14}(\d{2}[0-9x])?$     
#?重复0次或者1次,当是0次的时候是15位,是1的时候是18位
 
(3)^([1-9]\d{16}[0-9x]|[1-9]\d{14})$
#表示先匹配[1-9]\d{16}[0-9x]如果没有匹配上就匹配[1-9]\d{14}   
举个例子,比如html源码中有<title>xxx</title>标签,用以前的知识,我们只能确定源码中的<title>和</title>是固定不变的。因此,如果想获取页面标题(xxx),充其量只能写一个类似于这样的表达式:<title>.*</title>,而这样写匹配出来的是完整的<title>xxx</title>标签,并不是单纯的页面标题xxx。

       想解决以上问题,就要用到断言知识。

       在讲断言之前,读者应该先了解分组,这有助于理解断言。

       分组在正则中用()表示,根据小菜理解,分组的作用有两个:

 

       n  将某些规律看成是一组,然后进行组级别的重复,可以得到意想不到的效果。

       n  分组之后,可以通过后向引用简化表达式。

 

 

       先来看第一个作用,对于IP地址的匹配,简单的可以写为如下形式:

       \d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}

       但仔细观察,我们可以发现一定的规律,可以把.\d{1,3}看成一个整体,也就是把他们看成一组,再把这个组重复3次即可。表达式如下:

       \d{1,3}(.\d{1,3}){3}

       这样一看,就比较简洁了。

      

再来看第二个作用,就拿匹配<title>xxx</title>标签来说,简单的正则可以这样写:

       <title>.*</title>

       可以看出,上边表达式中有两个title,完全一样,其实可以通过分组简写。表达式如下:

       <(title)>.*</\1>

       这个例子实际上就是反向引用的实际应用。对于分组而言,整个表达式永远算作第0组,在本例中,第0组是<(title)>.*</\1>,然后从左到右,依次为分组编号,因此,(title)是第1组。

       用\1这种语法,可以引用某组的文本内容,\1当然就是引用第1组的文本内容了,这样一来,就可以简化正则表达式,只写一次title,把它放在组里,然后在后边引用即可。

       以此为启发,我们可不可以简化刚刚的IP地址正则表达式呢?原来的表达式为\d{1,3}(.\d{1,3}){3},里边的\d{1,3}重复了两次,如果利用后向引用简化,表达式如下:

       (\d{1,3})(.\1){3}

       简单的解释下,把\d{1,3}放在一组里,表示为(\d{1,3}),它是第1组,(.\1)是第2组,在第2组里通过\1语法,后向引用了第1组的文本内容。

       经过实际测试,会发现这样写是错误的,为什么呢?

       小菜一直在强调,后向引用,引用的仅仅是文本内容,而不是正则表达式!

       也就是说,组中的内容一旦匹配成功,后向引用,引用的就是匹配成功后的内容,引用的是结果,而不是表达式。

       因此,(\d{1,3})(.\1){3}这个表达式实际上匹配的是四个数都相同的IP地址,比如:123.123.123.123。

      

       至此,读者已经掌握了传说中的后向引用,就这么简单。

对于分组的理解
对分组的理解

 

分组命名:语法(?p<name>)  注意先命名,后正则

import  re
import re
ret=re.search('<(\w+)>\w+<(/\w+)>','<h1>hello</h1>')   
print(ret.group())
# 给分组起个名字。就用下面的分组命名,上面的方法和下面的分组命名是一样的,只不过就是给命了个名字
ret=re.search('<(?P<tag_name>\w+)>\w+</(?P=tag_name)>','<h1>hello</h1>')

#(?P=tag_name)就代表的是(\w+)  
print(ret.group()) # 了解(和上面的是一样的,是上面方式的那种简写) 
ret=re.search(r'<(\w+)>\w+</\1>','<h1>hello</h1>') 
print(ret.group(1))

 

转义符:

 

posted @ 2019-09-16 13:02  s686编程传  阅读(219)  评论(0编辑  收藏  举报