• 第二十二篇 正在表达式 re模块


    re模块******

    就本质而言,正则表达式时一种小型的,高度专业化的编程语言,在python里,它内嵌在python中,并通过re模块实现。正则表达式模式被编译成一系列的字节码。然后用C编写的匹配引擎执行。

    正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。

    re 模块使 Python 语言拥有全部的正则表达式功能。

     说白了,正则就是用来处理字符串的。

    简言之,正则就是给字符串进行模糊匹配

    正则的用途:
    1. 模糊匹配
    
    2. 应用场景如:一个文本里存了一堆身份证号,要找到北京市的且1990年后出生的人
    这个例子可以不用正则也能实现,但是用正则实现更简单
    举例正则就可以这么实现: 
    ^110.......1990 + *
    
    3. 还有一些场景,不用正则是实现不了。

    字符串匹配:普通字符、元字符(特殊符号)。

      1. 普通字符:大多数字符和字母都会自身匹配

    hello = 'nihaoa alex teacher'
    
    # 之前也学过其他的处理字符串的方法,比如find,split,但是这些方法都是精确查找,完全匹配的方法,如果想实现模糊查找就完不成目标了,然而正则就可以解决这个问题。
    print(hello.find('alex'))

      2. 元字符:. ^ $ * + ? {} [] () |  

    元字符给我们提供了很多模糊匹配的可能。

    学正则其实就学这些元字符和6个方法。

    findall(pattern, string, flags=0):   pattern表示匹配的规则,string表示要在哪个字符串里进行匹配。

    findall()会把所有匹配的结果都拿出来。

    • 第一个元字符 点 .

    点:是通配符,除了换行符 不能替代之外,其他什么都可以代替,如数字,字母,特殊字符等等。

    记住:一个点,只能代表一个字符,不能代表多个字符。

    import re
    hello = 'nihaoa alex teacher,a24x,a234x'
    
    # 匹配出以a开头,以x结尾的字符
    # 记住:一个点,只能代表一个字符,不能代表多个字符。
    print(re.findall('a..x', hello))            # ['alex', 'a24x']
    print(re.findall('a...x', hello))           # ['a234x']
    
    # 如果有多个字符需要用.匹配,怎么简写呢?
    [答】这个问题涉及到重复的问题,关于重复后面会有四个元字符来满足这个简写的功能。分别是 * + ? {}

    • 第二个元字符:^

    尖角号(^):表示以什么开头的意思。匹配字符串的开头,开头能匹配上,返回结果;开头匹配不上,就终止,返回空列表。

    import re
    hello1 = 'nihaoa alex teacher,a24x,a234x'
    hello2 = 'alexawwxteacher,a24x,a234x'
    
    
    # ^:匹配以 某字符串 开头。
    # 意思是,要去匹配hello1 和 hello2这两个字符串的开头,看他的开头是不是以 "a..x" 这样的四个字符串开头的
    # 能匹配到,就返回匹配结果;没有匹配到就返回空列表 print(re.findall('^a..x', hello1)) # [] print(re.findall('^a..x', hello2)) # ['alex']
    • 第三个元字符:$

    美元符($):表示以什么字符串结尾的意思。匹配字符串的末尾。

    import re
    hello1 = 'nihaoa alex teacher,a24x,a234x'
    hello2 = 'alexawwxteacher,a24x,a234x'
    hello3 = 'alexawwxteacher,a24x,a234x$'
    
    # $: 匹配以 某字符串结尾
    # 意思是,要去匹配hello1 和 hello2这两个字符串的结尾,看他们的结尾是不是以 "a...x" 这样的五个字符串结尾的
    
    print(re.findall('a..x$', hello1))   # []
    print(re.findall('a...x$', hello2))   # ['a234x']
    print(re.findall('a...x$', hello3))   # []
    • 第四个元字符:*
    • *是按照星号紧挨着的字符去重复, 重复范围是0到无穷次。匹配的时候是按照最多的来匹配

    import re
    
    hello = 'assddjkfdsdddddsdfdadffffffddd'
    
    # * 是匹配0到无穷次的内容
    print(re.findall('d*', hello))
    # 结果
    ['', '', '', 'dd', '', '', '', 'd', '', 'ddddd', '', 'd', '', 'd', '', 'd', '', '', '', '', '', '', 'ddd', '']
    
    # 预期的结果好像与我们的想想不一样,为什么?
    # 【答】因为 * 的匹配范围是[0,无穷大),所以第一个字符是a, 也算是匹配上了。
    
    #上面的例子并不好,换一个
    import re
    hello = 'dddfjakffdjkfddfderqerqeddddd'
    
    print(re.findall('^d*', hello))
    # 结果
    ['ddd']
    
    # 与* 对应的有一个元字符,是 + 号
    • 第五个元字符:+

    + 号匹配的范围是[1,无穷次),意思是匹配字符至少得有一个。

    # 这个被匹配字符串都有x,看不出区别
    print(re.findall("alex*", "asdhfalexxxx"))    # ['alexxxx']
    print(re.findall("alex+", "asdhfalexxxx"))    # ['alexxxx']
    
    
    # 下面两个例子,被匹配字符串没有x,就能看出区别了
    
    print(re.findall("alex*", "asdhfale"))
    # 结果
    ['ale']
    # 解释:因为*的匹配范围是[0,无穷次),即使没有x,也算是匹配上了
    
    
    print(re.findall("alex+", "asdhfale"))
    # 结果
    []
    
    # 解释:因为 + 的匹配范围是[1,无穷次),+ 号紧挨着的字符,在被匹配字符里必须至少有一个,才能匹配上
    • 第六个元字符:?

    ?的匹配范围是[0,1],表示它可以匹配0次,或1次,只有这两个值。

    print(re.findall("alex?", "asdhfalexxxx"))    # ['alex']
    print(re.findall("alex?", "asdhfale"))        # ['ale']
    • 第七个元字符:{}

    {}的匹配范围是你自己定义的。

    {0,}:相当于 *

    {1,}:相当于 +

    {0,1}:相当于 ?

    {6}:相当于重复6次

    {1,6}:可以重复1,2,3,4,5,6里面的任何一个

    print(re.findall("alex{0,}", "asdhfalexxxx"))             # ['alexxxx']
    
    print(re.findall("alex{1,}", "asdhfalexxxx"))             # ['alexxxx']
    
    print(re.findall("alex{0,1}", "asdhfalexxxx"))            # ['alex']
    print(re.findall("alex{0,1}", "asdhfale"))                # ['ale']
    
    # 代表重复x6次
    print(re.findall("alex{6}", "asdhfalexxxx"))              # []
    print(re.findall("alex{6}", "asdhfalexxxxxxxx"))          # ['alexxxxxx']
    print(re.findall("alex{1,6}", "asdhfalexxxxxxxxxx"))      # ['alexxxxxx']

    再看几个例子,总结一下

    print(re.findall("a..in", "helloalvin"))
    # ['alvin']
    
    print(re.findall("^a...n", "alvinhelloalvin"))
    # ['alvin']
    
    print(re.findall("a...n$", "alvinhelloalvin"))
    # ['alvin']
    
    print(re.findall("abc*", "abcccc"))  # 贪婪匹配[0,+∞)
    # ['abcccc']
    
    print(re.findall("abc+", "abcccc"))  # 贪婪匹配[1,+∞)
    # ['abcccc']
    
    print(re.findall("abc?", "abcccc"))  # 匹配[0,1]
    # ['abc']
    
    print(re.findall("abc{1,3}", "abccc"))  #匹配[1,4]
    # ['abccc']
    • 惰性匹配

    注意:

    有 * + ? 的都是贪婪匹配,也就就是尽可能多的匹配;

    但是如果在 * +的后面加?可以使其变成惰性匹配,所谓惰性匹配,就是按照最少的去匹配

    print(re.findall("alex*?", "helloalvinalexxxxxxx"))
    # ['ale']
    # 解释:* 匹配范围是[0,+∞), 所以匹配上0次就可以算匹配成功了,*可以代表0了,所以匹配到ale就行了,后面就不匹配了
    
    print(re.findall("alex+?", "helloalvinalexxxxxxx"))
    # ['alex']
    # 解释:+ 匹配范围是[1,+∞), 所以匹配上1次就可以算匹配成功了,+可以代表1了,所以匹配到alex就行了,后面的x就不匹配了
    • 第八个元字符:[ ]    (********************的重要)

         [ ]的学名叫字符集,[ ]字符集的作用: 或的作用

    import re
    
    '''
    []字符集的第1个作用:或的作用
    '''
    print(re.findall("x[yz]", "x"))
    # []
    
    print(re.findall("x[yz]", "xy"))
    # ['xy']
    
    print(re.findall("x[yz]", "xyuuu"))
    # ['xy']
    
    print(re.findall("x[yz]", "xyuuxzuu"))
    # ['xy', 'xz']
    
    print(re.findall("x[yz]", "xzzzyyzz"))
    # ['xz']
    
    print(re.findall("x[yz]p", "xyzzpxzyzzp"))
    # []
    
    print(re.findall("x[yz]p", "xyzzpxzpyzz"))
    # ['xzp']
    
    print(re.findall("x[yz]p", "xypuuxzpuu"))
    # ['xyp', 'xzp']
    
    print(re.findall("x[y,z]p", "xypuuxzpuu"))
    # ['xyp', 'xzp']
    
    # []里面加个逗号(,), 没有特殊作用,与y z一样,就是个普通符合,构成了三种可以匹配的组合:xyp, x,p  xzp
    print(re.findall("x[y,z]p", "xypuuxzpuu,pu"))
    # ['xyp', 'xzp']
    print(re.findall("x[y,z]p", "xypuuxzpuux,pu"))
    # ['xyp', 'xzp', 'x,p']

       1. [ ] 字符集里有特殊功能的特殊符号: -

        - 的特殊意义:表示范围。

                 [ ] 字符集只有三个符号 -    ^        有特殊意义,其他符号都没有特殊意义。

    # 记住:[]里面没有特殊符号,除了 - ^  
    print(re.findall("q[a*z]", "kssadfkq"))
    # []    匹配不到,因为[]里至少还有一个字母需要匹配
    
    print(re.findall("q[a*z]", "kssadfkqaaa"))
    # ['qa']   前面讲过,*代表重复,但是放到[]字符集里,她就是个普通字符,没有其他特殊意义,构造三种组合 qa  q* qz
    
    print(re.findall("q[a*z]", "kssadfkqaaaq**"))
    # ['qa', 'q*']
    
    # 在[] 有特殊符号 -
    # 特殊意义:- 代表着范围,也就是a-z
    
    # 所以,下面的q后面随便跟一个a~z的字符,就能匹配到
    print(re.findall("q[a-z]", "qu"))
    # ['qu']
    
    # [a-z]也只代表一个字符,也就是可以是qa,qb,qc,qd,qe......qz等26个组合, 只要匹配到一个就停止匹配了
    print(re.findall("q[a-z]", "quiuo"))
    # ['qu']
    
    # [a-z]*:表示a-z 26个字母可以重复
    print(re.findall("q[a-z]*", "quiuofdfafad"))
    # ['quiuofdfafad']
    
    print(re.findall("q[a-z]*", "quiuofdfafad9"))
    # ['quiuofdfafad']
    
    #
    print(re.findall("q[0-9]*", "quiuofdffafad9"))
    # ['q']
    # 解释:
    # 对这个结果别意外, 虽然"quiuofdffafad9" q后面没有紧挨着的数字,
    # 但是 因为*的匹配范围是[0,+∞), 就是没匹配到的但也算匹配上了。所以结果是q
    
    print(re.findall("q[0-9]*", "quiuofdffqafad9"))
    # ['q', 'q']
    
    print(re.findall("q[0-9]*", "quiuq89ofdffqafad9"))
    # ['q', 'q89', 'q']
    
    print(re.findall("q[A-Z]*", "quiuq89ofdffqafad9"))
    # ['q', 'q', 'q']

       2. [ ] 字符集里有特殊功能的特殊符号: ^

          特别要注意的:[ ]字符集里的^ 与 上面讲的放在开头的^ 有天大的区别。

        放在[ ]里的^ 的特殊意义:表示 非。 举例 q[^a-z] 表示 只要不是a-z里的都能匹配上

    print(re.findall("q[^a-z]", "q123"))
    # ['q1']
    print(re.findall("q[^a-z]", "qab"))
    # []
    
    print(re.findall("q[^a-z]", "qz"))
    # []
    
    print(re.findall("q[^a-z]", "quiuq89ofdffqafad9"))
    # ['q8']
    
    # 需求:怎么把(2-1)提出来?
    print(re.findall("([^()]*)", "12+(34*6+2-5*(2-1))"))
    # ['(2-1)']
    
    # 解释:
    '''
    元字符()是有特使意义的,如果要把()变成普通字符,要加个 斜杠 
    1. (  表示以(开头,   )  表示以 )结尾
    2. 中间得是没有()的, 才代表着最里层。所以要写给字符集[ ],字符集里写[^()]:表示非(),且是一个字符
    3. 第一步和第二步已经匹配出来了以(开头,以)结尾,但是()里面有多少字符,都是啥你并不知道,所以要在[]外面加个 * 统配
    '''

       3. [ ] 字符集里有特殊功能的特殊符号: 转义付

        的特殊意义,也是最重要的一个:

             (1)能让有功能的符号变为没功能(后跟元字符,去重特殊功能, 比如  .

             (2)也能让没功能的符号变为有功能(后跟普通字符,实现特殊功能,比如 d)

    ####   让没有意义的字符,变得有意义####
    
    d: 匹配任何十进制数, d  代表 [0 -9 ] 的任何一个数字,只代表一个数字,相当于  [0-9]
    d+:匹配任何十进制数,  d+只代表多个数字组合在一起,相当于  [0-9]+
    D:匹配任何非数字字符, 相当于 [^0 - 9]
    
    print(re.findall("d", "12+(34*6+2-5*(2-1))"))
    # ['1', '2', '3', '4', '6', '2', '5', '2', '1']
    
    print(re.findall("d+", "12+(34*6+2-5*(2-1))"))
    # ['12', '34', '6', '2', '5', '2', '1']
    
    print(re.findall("D+", "12+(34*6+2-5*(2-1))"))
    # ['+(', '*', '+', '-', '*(', '-', '))']
    
    s: 匹配任何空白字符,相当于[	
    
    fv]
    S: 匹配任何非恐怖字符,相当于[^	
    
    fv]
    S+:匹配任何非恐怖字符,字符可以组合在一起
    
    # 可以取出空格
    print(re.findall("s", "hello wor  ld!"))
    # [' ', ' ', ' ']
    
    print(re.findall("s+", "hello world!"))
    # [' ']
    
    print(re.findall("S", "hello world!"))
    # ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!']
    print(re.findall("S+", "hello world!"))
    # ['hello', 'world!']
    
    w:匹配任何字母数字字符及下划线,相当于[a-zA-Z0-9_]
    W:匹配任何非字母数字字符及非下划线,相当于[^a-zA-Z0-9_]
    
    print(re.findall("w", "[a-zA-Z0-9_]"))
    # ['a', 'z', 'A', 'Z', '0', '9', '_']
    print(re.findall("w+", "[a-zA-Z0-9_]"))
    # ['a', 'zA', 'Z0', '9_']
    
    print(re.findall("W", "[a-zA-+Z0-9_]"))
    # ['[', '-', '-', '+', '-', ']']
    
    print(re.findall("W+", "[a-zA-+Z0-9_]"))
    # ['[', '-', '-+', '-', ']']
    
    :匹配一个特殊字符边界,比如 空格,&,#,@等等
    
    # 需求:匹配出一个 I
    print(re.findall("I", "I am LIST"))
    # []
    
    # 分析:上面拿不到,看I前后的特点,是I后有个空格,所以就可以用来匹配这个特殊符号空格了。
    print(re.findall("I", "I am LIST"))
    # [ ]
    # 为什么还是不行呢?
    
    # 匹配规则前面加个r,代表转义, r表示原生字符串,表示在Python这一层,里面的任何内容都不做转义了,就把代表空格的意思传给了re模块
    print(re.findall(r"I", "I am LIST"))
    # [ ]
    
    # 匹配规则里加个,也代表转义
    # 在re模块匹配规则里,\:等于去掉了的特殊意义,就把变成了要给普通的字符,然后,就表示空格,传给re模块就是真正的空格了。
    print(re.findall("I\b", "I am LIST"))
    
    # 问题:上面已经都代表了空格,为啥还匹配不到呢?
    # 【答】1. 如果只在re模块里,就代表了空格,是没问题的
    # 2. 但是,程序是要用Python解释器执行的,而在Python 解释器里是有特殊意义的,当Python执行到print(re.findall("I", "I am LIST"))的时候,就被python解释器解释成了Python的意思,而传给re模块就不再是空格了
    # 3. 所以,要通过 r"I"告诉python解释器不转义  或者 "I\b" 来转义
    # 所以,总结一下,你想匹配的内容,是需要re模块能认识的才行。
    
    # 再看个例子
    # 需求:匹配一个 cl, 是就是一个普通的字符
    # 想匹配的是 cl,首先在正则里面, 得是c\l, 通过多加一个把另一个正则
    # 里的转义 进行转义,转义成一个普通的.
    # 然而,程序是通过Python解释器解释的,那么也就是说python执行完该行程序# 
    # 后,需要给re里面传个c\l, 然后re才能 将c\l 转义成 cl这个普通字符。
    # 所以re.findall("c\l"),python解释器只给re传了一个进入,即cl是传给了re,
    # 而实际上,re需要\,所以,re.findall("c\\l"),python解释器自己会先转义一下剩下了c\l,这个再传给re,re里就变成了 c\l, 然后re自己再转义一次,就变成了最终想要的 cl的结果。
    
    print(re.findall("c\\l","nihaos cl"))  
    # ['c\l']
    
    # 当然,这么写实在是太麻烦了,可以通过再前面加个r,告诉python解释器,这个不需要转义,然后直接传给re的就是c\l了
    print(re.findall(r"c\l","nihaos cl"))
    # ['c\l']
    
    # 当然,这里返回结果又有疑问了,我们要匹配的是cl,可以返回结果是["c\l"]呀,不对啊。
    【答】其实,这是对的,因为匹配是再re里进行的,匹配成功并返回结果给python解释器这一层了,而传给python解释器又要进行python解释器这一次的转义,所以,看到的就是["c\l"].
    重要的不是你看到了几个\,重要的是匹配成功了。

     

    ####  让有意义的字符变得没意义####
    
    # 下面的匹配规则里,  . 可以代表任意字符,下面是匹配结果
    print(re.findall("www.baidu", "www.baidu"))    # ['www.baidu']  
    print(re.findall("www.baidu", "wwwobaidu"))    # ['wwwobaidu'] 
    print(re.findall("www.baidu", "www/baidu"))    # ['www/baidu']
    print(re.findall("www.baidu", "www
    baidu"))   # []       因为. 不能匹配换行符
    
    
    # 如果在匹配规则里的.前面加个\,即 .  就让.代表任意字符的功能消失掉了,这个. 此时就代表一个普通的字符.
    print(re.findall("www.baidu", "www.baidu"))
    # ['www.baidu']   # 所以这个是可以匹配出来的,因为被匹配字符串里有个. ,与匹配规则一致
    
    # 而下面三个就无法匹配上了
    print(re.findall("www.baidu", "wwwobaidu"))  # []
    print(re.findall("www.baidu", "www/baidu"))  # []
    print(re.findall("www.baidu", "www
    baidu"))  # []
    
    
    print(re.findall("www*baidu", "www*baidu"))   # []
    print(re.findall("www*baidu", "www*baidu"))   # ['www*baidu']
    •  第九个元字符:|

      | 管道符表示  或的意思,或 | 左边的一部分,或者|右边的一部分

      [ ] 字符集 也有或的作用

    print(re.findall(r"ka|b", "sdjkasf"))   # ['ka']
    print(re.findall(r"ka|b", "sdjka|bsf"))   # ['ka', 'b']
    print(re.findall(r"ka|bc", "sdjka|bsf"))   # ['ka']
    print(re.findall(r"ka|bc", "sdjka|bcsf"))   # ['ka', 'bc']
    • 第十个元字符:()

      ()表示分组

      再学分组之前,先看下search()方法和findall()方法的比较

    # search()方法:
    # 1. 在匹配字符串里,只要要到一个符合的结果就返回了,不会再继续往后找了;而且search()方法返回但是一个对象;
    # 2. 如果没有匹配到,就什么都不返回
    # 3. 返回的对象里,group()方法可以提取出返回的值
    
    # findall()方法:是把字符串里所有匹配到的字符串都放到一个列表里。
    
    print(re.findall("d+", "dfjad;fj123fjdfk33dfad3e23"))
    # ['123', '33', '3', '23']
    
    print(re.search("d{5}", "dfjad;fj123fjdfk33dfad3e23"))
    # None
    
    print(re.search("d+", "dfjad;fj123fjdfk33dfad3e23"))
    # <_sre.SRE_Match object; span=(8, 11), match='123'>
    
    # 怎么从返回对象里提取出想要的值呢?
    print(re.search("d+", "dfjad;fj123fjdfk33dfad3e23").group())
    # 123

      下面是分组的内容:

    # 
    print(re.findall("abc", "abcccc"))   # ['abc']
    print(re.findall("abc+", "abcccc"))   # ['abcccc']
    print(re.findall(r"abc+", "abcccc"))   # ['abcccc']
    
    # 无名分组
    # 上面的+重复的只是紧挨着+的c这一个字符
    # 但是现在想要让+重复abc这一个整体,就需要用到()进行分组了
    print(re.findall(r"(abc)+", "abcccc"))   # ['abc']
    print(re.findall(r"(abc)", "abcccc"))   # ['abc']
    
    print(re.findall(r"(abc)+", "abcabcabcc"))   # ['abc']
    print(re.findall("(abc)+", "abcabcabcc"))   # ['abc']
    # 其实匹配出的结果是['abcabdabc'],但因为是分组的,默认优先只展示分组里的,而分组里只有一个abc,所以显示了一个有括号的一部分:['abc']
    # 只出现一个abc,是因为分组了,默认只显示组内匹配的内容
    print(re.findall("(?:abc)+", "abcabcabcc")) # ['abcabcabcc"] 去除了默认显示分组匹配的内容 print(re.findall("(abc)", "abcabcabcc")) # ['abc', 'abc', 'abc'] print(re.findall("(abc)", "abcdefabcessabcc")) # ['abc', 'abc', 'abc'] print(re.findall("(abc)", "abcabcabcc")) # ['abc', 'abc', 'abc'] #有名分组 # 有名分组 print(re.findall("(?P<name>w+)","abcccc")) # ['abcccc'] print(re.findall("(?P<name>[a-z]+)","abcccc")) # ['abcccc'] print(re.findall("(?P<name>[a-z]+)","alex36wusir34xialv33")) #['alex', 'wusir', 'xialv'] print(re.findall("(?P<name>d+)","alex36wusir34xialv33")) # ['36', '34', '33'] # 下面这个是固定写法 # 匹配规则里"(?P<name>真正的匹配内容)" print(re.search("(?P<name>[a-z]+)","alex36wusir34xialv33").group()) # alex print(re.search("(?P<name>d+)","alex36wusir34xialv33").group()) # 36 print(re.search("(?P<name>[a-z]+)d+","alex36wusir34xialv33").group()) # alex36 # 去掉上面的?P<name>,结果并没有什么不同 print(re.search("[a-z]+","alex36wusir34xialv33").group()) # alex print(re.search("d+","alex36wusir34xialv33").group()) # 36 print(re.search("[a-z]+d+","alex36wusir34xialv33").group()) # alex36 # 那似乎?P<name>没有什么作用? # 对于真正要匹配的内容,的确是没有什么作用 # 那?P<name>的作用到底在哪里? # [答]是相当于把这个内容做了要给分组,每个匹配到的内容都可以通过name去拿了 print(re.search("(?P<name>[a-z]+)d+","alex36wusir34xialv33").group()) # alex36 # 上面是已经分组了,按照name分组了,匹配到的真正的信息是alex36, # 但如果只想取到alex,也就是只想拿到取出结果的name,也就是只想取分组的内容 print(re.search("(?P<name>[a-z]+)d+","alex36wusir34xialv33").group("name")) # alex print(re.search("(?P<name>[a-z]+)(?P<age>d+)","alex36wusir34xialv33").group()) # alex36 print(re.search("(?P<name>[a-z]+)(?P<age>d+)","alex36wusir34xialv33").group("age")) # 36

     re模块下常用的方法

    findall() 和 search()方法上面已经有例子了,不再重复

    • re.findall(): 返回所有满足匹配条件的结果,放在列表里
    • re.search() : 函数会在字符串内查找模式匹配,直到找到第一个匹配然后返回一个对象
      • 意义: 比如计算器程序的时候就比较有用
    • re.search().group():通过调用group()方法可以提取出匹配的字符串;如果字符串没有匹配,则返回None; group返回匹配到的第一个,groups返回匹配到的全部
    # 下面是个需要计算的表达式
    (12+(34*6+2-5*(2-1)+(8+9-8*4))
    
    # 都知道要先计算最里面的括号,但是这个表达式里最里面有2个括号,先计算哪一个?
    # 这时候就能用到search()的好处了,它只取第一个,然后计算出结果替换括号,如此
    # 反复匹配,计算,替换,直到所有的括号都处理完成。

    下面是re模块里还没介绍到的方法:

    • re.match():同search,不过仅仅在字符串开始处进行匹配。
      • match匹配成功了跟search一样,返回一个对象;
      • match没匹配成功,什么都不返回。
    print(re.match("d+", "alex36wuser33xialv33"))   # 什么都没返回,在命令行里演示最直观
    # 解释:什么都没返回,因为,match仅仅匹配开头,而开头是a,所以啥都匹配不到

    # 所以,match()方法完全可以被search()方法取代
    • re.split():对字符串进行分割
    # 按空格分割
    print(re.split(" ","hello abc def"))  # ['hello', 'abc', 'def']
    
    # 空格要分开,有管道符|的也要分开
    print(re.split("[ |]","hello abc|def"))  # ['hello', 'abc', 'def']
    
    print(re.split("[ab]", "abc"))       # ['', '', 'c']
    # 为什么会有空出现?看下面的例子
    # 解释:
    # 首先会按照 "a" 分,abc里a的左变没有内容,就是一个空,a的右边是bc, 这样按照'a'分,就分成了两部分['','bc']
    # 但是事还没完,因为是分割法是以a 或者 b分的,此时,再按b分,会把已经得到两部分['','bc']按照b再分,
    # 其中,空的一部分肯定没有b了,就分不了了,而bc中有b,可以分,其中b的左边没内容,就是'',右边只有'c'
    # 所以最后的结果是 ['', '', 'c']
    
    # 按照a 或者 b 分
    print(re.split("[ab]", "asdabcd"))       # ['', 'sd', '', 'cd']
    # 分割步骤:
    # 1. 按a分,得到['','sdabcd']
    # 2. 第二部分还有a,再按a分一次,得到['','sd','bcd']
    # 3. 再按b分,得到['','sd','','cd']
    • re.sub(pattern, repl, string, count=0, flags=0):替换
      • pattern:匹配的条件
      • repl:需要替换成的内容
      • string:被匹配字符串
      • count:匹配次数
      • flags:
    # 需求,把字符串里所有的数字替换为A
    
    print(re.sub("d", "A", "jsadfja432143jkjfadkn343413"))
    # jsadfjaAAAAAAjkjfadknAAAAAA
    
    print(re.sub("d+", "A", "jsadfja432143jkjfadkn343413"))
    # jsadfjaAjkjfadknA
    
    print(re.sub("d", "A", "jsadfja432143jkjfadkn343413",4))  # 4代表,匹配的次数,匹配次数够了,后面的就不再匹配和替换了
    # jsadfjaAAAA43jkjfadkn343413
    • re.subn():返回共匹配了多少次,返回结果是元组
    print(re.subn("d+", "A", "jsadfja432143jkjfadkn343413"))
    # ('jsadfjaAjkjfadknA', 2)
    
    print(re.subn("d", "A", "jsadfja432143jkjfadkn343413"))
    # ('jsadfjaAAAAAAjkjfadknAAAAAA', 12)
    • re.compile():compile()里只有一个参数,就是匹配规则
    # compile:是编译的意思
    a = re.compile("d+")   # 先把这个匹配规则编译好,供后面的匹配直接调用就可以了
    
    print(a.findall("fajfdaj34124134fadjflkjadfj23"))
    # ['34124134', '23']
    
    print(a.findall("民132nihakl4543na你啊打发343"))
    # ['132', '4543', '343']
    
    # 所以,当匹配一次的时候,用compile()和不用compile()没有什么区别,但是如果需要多次匹配不同的字符串,
    # 如果都不用compile(),那每次匹配都会先编译一次规则,效率就会降低,
    # 而如果用compile()先把规则编译好,再之后的多次匹配里直接调用,那效率就高了
    • re.finditer():与findall()方法一样,都是匹配全部字符串,区别在于:
      • findall()匹配后返回的结果放在了列表里
      • finditer()匹配后的结果放在了一个迭代器对象里,返回的是个地址
      • finditer()与findall()相比有什么好处呢?
        • 当处理的数据非常庞大的时候,findall()都存在列表里肯定会耗内存,而finditer()直接存在一个迭代器对象里,只有当需要用里面的数据的时候再一条一条的拿出来,节省内存
        • 用next().group()方法可以一条条的取出数据
    a = re.compile("d+")
    b = a.finditer("fjadjf12323nfnnjfal123")
    print(b)   # <callable_iterator object at 0x00D451D0>
    
    # 如何取出迭代器对象里的结果?
    # 用next().group()方法
    
    print(next(b).group())  # 12323
    print(next(b).group())  # 123
    
    # print(next(b))    # <_sre.SRE_Match object; span=(6, 11), match='12323'>
    • 匹配规则里还有分组时的两种情况:
      • 1. 默认展示分组内符合匹配结果的内容
      • 2.分组内加?: 可以去除默认规则  
    # 匹配规则里包含分组时,默认匹配结果只展示分组内的匹配结果
    # 原理:程序认为,既然你分组了,那你肯定是最想看到分组里的匹配结果的,所以展示分组内的匹配结果
    print(re.findall("www.(baidu|163).com", "www.baidu.com"))  # ['baidu']
    print(re.findall("www.(baidu|163).com", "www.baidu.com"))   # ['baidu']
    print(re.findall("www.(baidu|163).com", "www.163.com"))   # ['163']
    
    # 如果不想只展示分组内的匹配结果,那么需要再分组内加上?:,来去除由默认规则,就会展示全部符合匹配规则的结果
    print(re.findall("www.(?:baidu|163).com", "www.baidu.com"))   # ['www.baidu.com']
    print(re.findall("www.(?:baidu|163).com", "www.163.com"))   # ['www.163.com']

     关于模块导入的一点补充:

    需求:把上面目录下的test.py导入到bin.py里

    import sbsb
    # sbsb.py 是导入的文件,是一个相对路径,既然是相对路径,总是相对于谁存在的。那么他到底是相对谁而存在呢?
    # 相对路径最终是相对于当前路径(也就是bin的路径F:workspaceOldBoy源码课件与源码python全栈3期-课件与源码day23in.py 去掉bin.py
    # 这个文件名后,就变成当前路径了)继续拼接
    # F:workspaceOldBoy源码课件与源码python全栈3期-课件与源码day23sbsb.py
    
    '''
    import 导入的原理:
    1. import 干的第一件事是帮过你去找到文件,通过上面路径拼接的方法找到文件
        ('F:workspaceOldBoy源码课件与源码python全栈3期-课件与源码day23sbsb.py')
    2. import 干的第二件事是把导入的文件test.py copy 出来执行一遍
    
    '''
    
    # 上面执行程序会报错

     解决办法是:

    from module import sbsb
    # from 做的工作,是生成这样一个相对路径,  module/sbsb.py
    # 然后与当前路径拼接成文件的最终路径:
    F:workspaceOldBoy源码课件与源码python全栈3期-课件与源码day23modulesbsb.py
    
    这样执行就不会报上面 No module named  'sbsb' 这个错,但是却报了下面的错

     因为在sbsb.py文件里,导入了cal 模块,见下图。

    那报上面错误的根本原因是什么呢?

    还记得上面说的吧,import导入首先会做的两件事:
    '''
    1. import 干的第一件事是帮过你去找到文件,通过上面路径拼接的方法找到文件
        ('F:workspaceOldBoy源码课件与源码python全栈3期-课件与源码day23modulesbsb.py')
    2. import 干的第二件事是把导入的文件test.py copy 出来执行一遍
    
    '''
    
    问题就出在做第二件事的时候,copy出sbsb.py文件在bin.py里执行的时候,sbsb.py首先是导入cal.py文件的,那也就是要先找cal.py的路径,最终拼成绝对路径。
    记住了,相对路径前的绝对路径永远都是当前执行文件的绝对路径,甭管你套了多少次的导入,永远都是当前执行文件(bin)的路径。所以,最后拼的路径是:
    'F:workspaceOldBoy源码课件与源码python全栈3期-课件与源码day23cal.py'
    这个路径当然找不到了,那怎么解决呢?
    在sbsb.py文件里,把导入语句改写成:
    
    from module import cal

    这样虽然解决了在bin.py文件下运行不保错的问题,但是如果在sbsb.py下运行,又会报错,因为此时sbsb.py变成了当前文件,运行时路径就被拼接成了:'F:workspaceOldBoy源码课件与源码python全栈3期-课件与源码day23modulemodulecal.py' ,所以会报错。

    备注

    这个补充主要是为了说明,导入模块的原理:

    1. 怎么识别当前路径与相对路径

    2. 当前路径与相对路径的拼接

     

  • 相关阅读:
    python-pytest使用
    PyCharm2021使用教程 --- 11、PyCharm必备插件
    PyCharm2021使用教程 --- 10、PyCharm中实用小技巧
    PyCharm2021使用教程 ---9、PyCharm中的搜索技巧(文件/函数/内容)
    PyCharm2021使用教程 --- 8、版本控制
    PyCharm2021使用教程 --- 7、如何使用DeBug调试程序
    PyCharm2021使用教程 --- 6、如何运行程序
    PyCharm2021使用教程 --- 5、PyCharm的基本配置
    PyCharm2021使用教程 --- 4、菜单栏详细介绍
    PyCharm2021使用教程 --- 3、使用pycharm创建项目
  • 原文地址:https://www.cnblogs.com/victorm/p/9262924.html
Copyright © 2020-2023  润新知