python正则表达式学习拾遗

作者: My熊猫眼 | 来源:发表于2020-06-06 20:12 被阅读0次

    本篇学习笔记参考re——正则表达式 所作,不明白之处可以参阅上述的官方文档。
    因为是个人学习笔记,所以并不会完全介绍所有的正则表达式,而是查漏补缺性质的.
    A.
    在正则匹配的时候,对于匹配数量的控制,常用的特殊字符有 * ? + {m,n} 这4种. 但是这些都是属于“贪婪”匹配,也就是说,这些字符会尽可能多的去进行匹配。 而在实际的情形中,我们可能想进行尽可能少的匹配。这时候有一个办法,就是在这4种匹配的后面跟上 ? 符号。 比如下面的例子:

    >>> myfunc="__myfunc__"
    >>> re.findall(r'_.*',myfunc)    #默认为“贪婪”匹配,所以匹配了整个字符串. 然后就没有需要匹配的了.
    ['__myfunc__']
    >>> re.findall(r'_.*?',myfunc)  #此时为最小匹配,所以每次匹配的结果只有一个字符‘_', 然后对剩下的字符串继续进行最小匹配,一共获得了4次匹配的结果。
    ['_', '_', '_', '_']
    >>> 
    

    B.
    [] 该符号的内部表示字符的集合. 其中集合中的任意字符进行匹配。
    1). 在该符号的内部,特殊符号都变成了普通字符。
    2). 其中 - 在集合内部的头部或者末尾的时候,才表示普通字符。
    3). 该集合的内部支持 字符类,比如 [\t] 表示其中一个字符是 tab键的键值
    4). 和POSIX 一样,集合内部的 ^ 如果是在首位,那么表示取反,否则是普通字符
    5). 如果集合中含有[ , ] 符号,那么用转义符更方便,虽然官方也支持其他的解决办法,个人更乐意用转义符号.
    C.
    A|B 这个匹配表示逻辑或,但是 只有 A匹配失败的时候才会匹配B,当然如果有更多个表达式需要匹配,那么可以继续用 | 进行连接,但从左到右,只要匹配成功,那么右边的表达式就不会继续进行匹配.
    D.
    (?...) 这是个扩展标记法 (一个 '?' 跟随 '(' 并无含义)。这种扩展通常并不创建新的组合;简单点理解就是: 对正则表达式设置一些 flag, 不同版本的python 可能略有不同,具体可以用命令查看:

    [ i for i in dir(re) if len(i)==1]      #我的环境输出为: ['I', 'L', 'M', 'S', 'T', 'U', 'X']
    

    但是在使用 (?...) 这种形式的时候,对应的flag标志都需要小写,只有一个L需要大写,这里原因不清楚,估计是为了更方便的进行识别吧?毕竟大写的i和小写的L在部分字体中是很难通过肉眼区分的.
    对于设置正则表达式的flag, 除了使用 (?...) 的方式外,还可以在每个方法中进行指定,比如下列两种方式使用findall方法的结果相同:

    >>> re.findall(r"china","China,CHINA,china",re.I)  #传递给findall 方法flag参数  
    ['China', 'CHINA', 'china']
    >>> re.findall(r"(?i)china","China,CHINA,china") #使用 (?...) 的方式,无需传递给findall 方法对应的flag 参数.
    ['China', 'CHINA', 'china']
    >>> 
    

    E.
    (?:…) 一般情况下,通过() 实现正则表达式的分组,然后在后面可以通过 \n 的方式进行引用,但是如果不希望分组被引用,那么可以用 (?:...)的方式进行分组,这个非常有用哦!
    F.
    在对复杂的字符串进行正则匹配的时候,我们可能需要”对匹配到的内容进行提取或者引用“,这时候最好的方式就是对匹配的内容取个名字,然后引用这个名称 就可以了,Python3.6开始就提供了这种实现:
    1). 首先对匹配的内容取一个名字,语法为 (?P<Defined_Name>...),其中 ... 表示匹配的内容,Defined_Name就是定义的名称,这个名称根据你的喜好而定,只要这个名称是有效的python标识符就可以.
    简单理解就是:
    对于你需要定义名称的正则表达式,需要用() 引用起来,然后写成 (?P<Defined_Name>) 的格式就可以了, 在名称定义好之后,那么就可以进行引用了,目前有三种方式来引用:
    a. 在同一正则表达式内引用的语法为: (?P=Defined_Name), 例子如下:

    >>> s="https://docs.python.org/zh-cn/3.6/library/re.html"        
    >>> s2="https://docs.python.org/zh-cn/zh-cn/3.6/library/re.html"
    >>> p=r'(.*?)//(.*?)/(?P<myregex>.*?)/(?P=myregex).*?html'  #这里使用 myregex 作上述的Defined_Name.  在同一个正则表达式中对myregex 的引用方式为: (?P=myregex)
    >>> re.search(p,s)   #对字符串s, 没有匹配结果
    >>> re.search(p,s2)  #对字符串s2,有匹配结果。
    <_sre.SRE_Match object; span=(0, 55), match='https://docs.python.org/zh-cn/zh-cn/3.6/library/r>
    >>>                                   
    

    b. 对于匹配的对象,那么可以用其 group 方法,然后传递 Defined_Name 作为参数,从而实现引用, 这时候 Defined_Name 做为一个参数,必须是字符串。接着看下面的例子:

    >>> r=re.search(p,s2)     #匹配结果保存到 r 中。
    >>> r.group("myregex")  #通过group 方法来取 匹配的结果.
    'zh-cn'
    

    c. (?P<>) 其实在正则表达式的处理过程中,也是一个有效的分组,所以,我们可以通过 使用分组的方式来引用上述定义了名称的正则表达式,在re.sub方法中,可以用如下的方式:

    >>> p
    '(.*?)//(.*?)/(?P<myregex>.*?)/(?P=myregex).*?html'            
    >>> s2
    'https://docs.python.org/zh-cn/zh-cn/3.6/library/re.html'
    >>> re.sub(p,"\g<myregex>",s2)            # 正则表达式p 匹配整个s2字符串,然后这里就是把整个字符串s2 替换为 group: myregex 所匹配的内容. 
    'zh-cn'
    >>> re.sub(p,"\g<3>",s2)   #因为myregex group 对应的是第三个分组,所以可以直接用 分组3 来写,结果是一样的。
    'zh-cn'   
    >>>                                    
    

    G.
    (?=…) (?!…) (?<=…) (?<!…) , 这4个表达式,个人认为应该划分为同一个组,都有一个共性: 不占用匹配的位置,仅仅限定了匹配的条件,从这一点上看,有点类似 ^ 和 $ 的意味. 我们很容易注意到后面两个表达式中有”小于号“, 这里的小于号表示前向匹配,毕竟他们不占用匹配的位置,因此这个匹配的条件需要指明:究竟是前向匹配还是后向匹配。 所以另外两个没有”小于号”的就属于后向匹配了。至于“=” 和 “!” 自然就是表示匹配和不匹配了. 这样来理解,个人觉得更容易一些,那么让我们看一些例子吧(注意报错):

    >>> s="https://www.jianshu.com/writer#/notes/39570469/notes/fakeurl/preview"
    >>> re.findall("(?<=.*#/)notes",s)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "E:\Program files\Python\Python36\lib\re.py", line 222, in findall
        return _compile(pattern, flags).findall(string)
      File "E:\Program files\Python\Python36\lib\re.py", line 301, in _compile
        p = sre_compile.compile(pattern, flags)
      File "E:\Program files\Python\Python36\lib\sre_compile.py", line 566, in compile
        code = _code(p, flags)
      File "E:\Program files\Python\Python36\lib\sre_compile.py", line 551, in _code
        _compile(code, p.data, flags)
      File "E:\Program files\Python\Python36\lib\sre_compile.py", line 160, in _compile
        raise error("look-behind requires fixed-width pattern")
    sre_constants.error: look-behind requires fixed-width pattern   #这里的报错告诉我们“需要固定长度的pattern". 我们修改下试试!
    >>> re.findall("(?<=#/)notes",s)    #这下就成功匹配到了,匹配的结果只有notes, #/ 就两个字符的长度,所以上面的报错就是指  (?<=...) 中 ... 所代表的必须是固定长度. 并且 #/ 不占用匹配的结果。
    ['notes']
    >>>   
    >>> re.findall("(?<!#/)notes",s)    #验证  (?<!...) 的结果.
    ['notes']                                                
    

    验证完了前向匹配条件,我们来验证 后向匹配的表达式,注意看下面的测试结果:

    >>> s
    'https://www.jianshu.com/writer#/notes/39570469/notes/fakeurl/preview'
    >>> re.findall("(?=/[0-9]{8})notes",s)
    []             #很奇怪,结果居然是空的,不应该是 notes 吗? 想一下 ^ 指定匹配的字符串的开头,那么必须写在字符串的最开始,而 $ 用于指定字符串的结尾条件,那么$必须写在字符串的末尾,所以 (?=...) 作为后向匹配的条件自然要写到 被匹配字符串的后面,那么 我们修改下看看: 
    >>> re.findall("notes(?=/[0-9]{8})",s)           #这下结果就正常了.
    ['notes']
    >>> re.findall("notes(?!/[0-9]{8})",s)           #这个结果也是正常的
    ['notes']
    >>>                                                                      
    

    H.
    关于python中的一些控制字符:
    \b 和 \B 都是边界控制字符,对于匹配的字符串的相邻字符进行筛选,从而实现更精准的匹配. 在这里涉及到正则表达式中的”word“的概念。
    在ansic 编码下,对word的定义是: 数字,字母,以及下划线组成的字符串,其他的字符不属于word的组成部分。 而 \b 表示匹配的字符串其相邻的字符 不能是word的组成,否则不匹配。而\B 则表示,其匹配的字符串的 相邻字符必须是word的组成,否则不匹配,我们看下面的例子:

    >>> re.findall(r"(?i)\bchina\b","China,china_,CHINA,China0,0china,MChInAN") #china 单词的相邻字符不能是word的组成.
    ['China', 'CHINA']
    >>> re.findall(r"(?i)\Bchina\B","China,china_,CHINA,China0,0china,MChInAN")  #china单词的相邻字符必须是word的组成.
    ['ChInA']
    >>> re.findall(r"(?i)\bchina\B","China,china_,CHINA,China0,0china,MChInAN") #china单词的前面不能是word的组成,而后面必须是word的组成字符.
    ['china', 'China']
    >>> re.findall(r"(?i)\Bchina\b","China,china_,CHINA,China0,0china,MChInAN") #china 单词的前面必须是word的组成,而后面不能是word的组成.
    ['china']
    >>>                                                                                    
    

    \d , \D 这两个就比较简单了,前面表示数字,后面表示非数字.
    \w 就表示上述所述的word. \W 就是\w的取反.

    其他更多内容,请参考python的官方文档或者帮助,希望读到此片文章的你也可以获益哦!

    本文原创,转载请注明出处

    相关文章

      网友评论

        本文标题:python正则表达式学习拾遗

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