菜鸟学习Python(第十期)~~正则表达式(二)

作者: KangSmit的算法那些事儿 | 来源:发表于2019-10-01 23:33 被阅读0次

    继续上一次正则表达式(一)学习~~
    Python(第十期)~~正则表达式(一)

    回顾一段代码:

    >>> import re
    >>> re.search(r'YOU.', 'I LOVE YOU')
    >>> re.search(r'YOU.', 'I LOVE YOU.')
    <re.Match object; span=(7, 11), match='YOU.'>
    >>> re.search(r'YOU.', 'I LOVE YOU')
    >>> re.search(r'I.', 'I LOVE YOU')
    <re.Match object; span=(0, 2), match='I '>
    >>> re.search(r'YOU.', 'I LOVE YOU ')
    <re.Match object; span=(7, 11), match='YOU '>
    >>> 
    
    
    10、反斜杠“\”

    想要消除一个字符串的特殊功能,就在前面加上 反斜杠(\), 当然,反斜杠也带来特殊字符串的一些功能。

    >>> re.search(r'.', 'I LOVE YOU. ')
    <re.Match object; span=(0, 1), match='I'>
    >>> re.search(r'\.', 'I LOVE YOU. ')
    <re.Match object; span=(10, 11), match='.'>
    

    这里 ‘.’ 匹配的就是 点号(.)本身了,这时候,点号.不代表任何其他字符,它只代表点号,前面的反斜杠已经将其解译了。
    也就是说,在正则表达式中,反斜杠同样具有剥夺元字符的特殊功能的能力.(什么是元字符后面会讲到)。

    值得注意的是,反斜杠 \ 还可以使得普通的字符具有特殊能力,它能匹配数字(暗示了可以匹配IP地址,换句话说有数字的基本都能匹配),在学习Python经常使用\d就是为了匹配数字,其实\d就是正则表达式的一部分。下面使用 ‘\d’ 来匹配任何数字(只举几例),如:

    (1)数字:
    >>> re.search(r'\d.', 'I LOVE YOU 1314. ')
    <re.Match object; span=(11, 13), match='13'>
    >>> re.search(r'\d\d.', 'I LOVE YOU 1314. ')
    <re.Match object; span=(11, 14), match='131'>
    >>> re.search(r'\d\d\d.', 'I LOVE YOU 1314. ')
    <re.Match object; span=(11, 15), match='1314'>
    
    (2)IP地址

    可以打开这个网站一键获取自己正在使用的IP地址
    http://www.whatismyip.com.tw/
    https://whoer.net/zh
    点击进入如

    下面是我开始匹配IP地址

    >>> re.search(r'\d\d\d.\d\d\d.\d\d\d.\d\d\d', '103.16.26.148')
    >>> re.search(r'\d\d\d.\d\d.\d\d.\d\d\d', '103.16.26.148')
    <re.Match object; span=(0, 13), match='103.16.26.148'>
    >>> re.search(r'\d\d\d\.\d\d\d\.\d\d\d\.\d\d\d', '192.168.111.123')
    <re.Match object; span=(0, 15), match='192.168.111.123'>
    

    首先,\d 表示匹配的数字是 0~9,但是 ip 地址的约定范围每组数字的范围是 0~255,那你这里 \d\d\d 最大匹配数字是 999 ,而 ip 地址的最大范围是 255;然后,你这里要求 ip 地址每组必须是三位数字,但实际上有些 ip 地址中的某组数字只有 1 位或者 2 位,像这种情况,我们就匹配不了了,

    所以要做适当的 \d 数量的调整。

    当然也还有其他解决办法,下面给出字符类:

    11、字符类

    表示一个字符串的范围,需要创建一个叫做 字符类 的东西,使用中括号[ ] 来创建一个字符类,字符类的含义就是你只要匹配字符类中的一个字符,那么就匹配成功了,如:

    (1)匹配 元音字母(aeiou)
    >>> re.search(r'[aeiou]', 'i love you')
    <re.Match object; span=(0, 1), match='i'>
    >>> re.search(r'[aeiou]', 'I love YOU')
    <re.Match object; span=(3, 4), match='o'>
    >>> re.search(r'[aeiou]', 'I LOVe YOU')
    <re.Match object; span=(5, 6), match='e'>
    >>> re.search(r'[aeiou]', 'I LOVE YOu')
    <re.Match object; span=(9, 10), match='u'>
    

    值得注意的是,元音字母的匹配不能匹配大写的,严格按照英语的元音写法来进行匹配。这是因为正则表达式 是默认开启 大小字母敏感 模式的,所以 大写 ‘I’ 和小写 ‘i’ 会区分开来。解决的方案有两种,一种是关闭大小写敏感模式(后边进行讲解),另一种是修改我们的字符类[aeiou]为[aeiouAEIOU]。

    如果修改大小模式则可以匹配:

    >>> re.search(r'[aeiouAEIOU]', 'I LOVE YOu')
    <re.Match object; span=(0, 1), match='I'>
    >>> re.search(r'[AEIOU]', 'I LOVE YOu')
    <re.Match object; span=(0, 1), match='I'>
    >>> re.search(r'[AEIOU]', 'i LOVE YOu')
    <re.Match object; span=(3, 4), match='O'>
    
    (2)表示一个范围

    可以在字符类中使用 横杆或减号 ‘-’ 表示一个范围,如:
    (2.1)字母范围

    >>> re.search(r'[a-z]', 'i LOVE YOU')
    <re.Match object; span=(0, 1), match='i'>
    >>> re.search(r'[A-Z]', 'i LOVE YOU')
    <re.Match object; span=(2, 3), match='L'>
    >>> re.search(r'[A-Z]', 'i love You')
    <re.Match object; span=(7, 8), match='Y'>
    

    (2.2)数值范围

    >>> re.search(r'[1-5]', 'i love You 1314')
    <re.Match object; span=(11, 12), match='1'>
    >>> re.search(r'[4-5]', 'i love You 1314')
    <re.Match object; span=(14, 15), match='4'>
    >>> re.search(r'[3-4]', 'i love You 1314')
    <re.Match object; span=(12, 13), match='3'>
    
    12 、匹配次数

    限定重复匹配的次数,我们可以使用 大括号 来解决,举例:

    >>> re.search(r'BCD{3}E', 'BCDDDE')
    <re.Match object; span=(0, 6), match='BCDDDE'>
    >>> re.search(r'ab{3}c', 'abbbc')
    <re.Match object; span=(0, 5), match='abbbc'>
    

    其中 {3} 就表示前面的 D或b匹配时要重复3次,即BCDDDE和abbbc。
    值得注意的是,大括号里还可以给出重复匹配次数的范围,例如{a, b} 表示匹配 a 到 b 次。

    >>> re.search(r'ab{2,10}c', 'abbc')
    <re.Match object; span=(0, 4), match='abbc'>
    >>> re.search(r'BCD{2,10}E', 'BBCDDDE')
    <re.Match object; span=(1, 7), match='BCDDDE'>
    

    匹配 0~255:
    使用正则表达式来匹配 0~255

    >>> re.search(r'[0-255]', '134')
    <re.Match object; span=(0, 1), match='1'>
    

    但是我们想匹配 的是134 ,结果只是匹配到了 1。
    解释:
    切记:正则表达式 匹配的是字符串,所以呢,数字对于字符来说,只有 0~9,例如 134,就是由1、3、4 这三个字符来组成的,并没有说个 十百千 这些单位。因此,[0-255] 这个字符类(其中0-2,指的是 0 1 2,后面两个 5 就重复了)表示的是[0125]这四个数字其中的某一个,由此 re.search(r'[0-255]', '134') 就只匹配到了一个 1。

    下面解决这个问题,应该这么写:

    re.search('[01]\d\d|2[0-4]\d|25[0-5]', '134')
    <re.Match object; span=(0, 3), match='134'>
    

    源码剖析:
    这里匹配的正则表达式就是 [01]\d\d 或者 2[0-4]\d 或者 25[0-5],可以发现其中任何一个都是成立的。这里的 “或 ” 和 C语言中的 “或” 是一样用意的。
    不过,需要知道是上面的写法还是存在问题,要求匹配的数字必须是 3 位的,类似的我们可以输入如下匹配内容会发现无法匹配:

    >>> re.search('[01]\d\d|2[0-4]\d|25[0-5]', '4')
    >>> re.search('[01]\d\d|2[0-4]\d|25[0-5]', '1')
    >>> #None空的
    >>> re.search('[01]\d\d|2[0-4]\d|25[0-5]', '13')
    >>> 
    

    因此下面可以这样改写,让前面的两位数可以重复 0 次(因为默认是重复1次嘛):

    >>> re.search('[01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]', '1')
    <re.Match object; span=(0, 1), match='1'>
    >>> re.search('[01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]', '13')
    <re.Match object; span=(0, 2), match='13'>
    >>> re.search('[01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]', '134')
    <re.Match object; span=(0, 3), match='134'>
    

    因此,我们就可以来匹配一个 IP 地址啦:

    >>> re.search('(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}[01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]', '192.168.42.1')
    <re.Match object; span=(0, 12), match='192.168.42.1'>
    >>> re.search('(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}[01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]', '103.16.26.148')
    <re.Match object; span=(0, 13), match='103.16.26.148'>
    

    源码剖析:
    上面的小括号的意思就是分组,首先 ([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]) 作为一个组,然后加上 点号,(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5]).) 作为新的组,然后这个组重复3次,最后再加一组数字。这样就完成了一对 ip 地址的匹配啦。

    每日三道题, 笔试不吃亏:

    题目7:如何将一个列表的数据复制到另一个列表中?
    程序分析:使用列表[ : ], 事实上就是一种赋值的行为。

    >>> a = [1314, 520, 3.1415926]
    >>> b = a[:]
    >>> print(b)
    [1314, 520, 3.1415926]
    

    题目8:输出 九九 乘法口诀表。
    程序分析:分别对行和列考虑,共9行9列,i设为控制行,j设为控制列。

    for i in range(1, 10):
        for j in range(1, i+1):
            print("%d*%d=%-3d"%(i, j, i*j), end=" ")
        print("第%d行:"%i)
    

    执行结果:

    1*1=1   第1行:
    2*1=2   2*2=4   第2行:
    3*1=3   3*2=6   3*3=9   第3行:
    4*1=4   4*2=8   4*3=12  4*4=16  第4行:
    5*1=5   5*2=10  5*3=15  5*4=20  5*5=25  第5行:
    6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36  第6行:
    7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49  第7行:
    8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64  第8行:
    9*1=9   9*2=18  9*3=27  9*4=36  9*5=45  9*6=54  9*7=63  9*8=72  9*9=81  第9行:
    

    题目9:暂停一秒输出。
    程序分析:使用 time 模块的 sleep() 函数

    import time
     
    dict1 = {1998: '值得纪念的日子', 2019: '70周年'}
    for key, value in dict.items(dict1):
        print(key, value)
        time.sleep(2) # 暂停 2 秒
    

    执行结果:

    1998 值得纪念的日子
    2019 70周年
    >>> 
    

    补充题:暂停一秒输出,并格式化当前时间。

    import time
    #使用年月日语法
    print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
    # 暂停2秒
    time.sleep(2)
    print("格式化当前时间:", time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))
    

    执行结果:

    2019-10-01 23:30:02
    格式化当前时间: 2019-10-01 23:30:04
    >>> 
    

    相关文章

      网友评论

        本文标题:菜鸟学习Python(第十期)~~正则表达式(二)

        本文链接:https://www.haomeiwen.com/subject/xstlpctx.html