zoukankan      html  css  js  c++  java
  • 正则表达式分组()、不捕获(?:)和断言(?<=)详解

    原文链接:  https://www.cnblogs.com/leezhxing/p/4333773.html

    分组

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

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

    2.分组之后,可以通过后向引用简化表达式(1 或者$1)。

    分组举列

    先来看第一个作用,对于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这种语法,可以引用某组的文本内容,但不能引用正则表达式。

    例如刚刚的IP地址正则表达式为d{1,3}(.d{1,3}){3},里边的d{1,3}重复了两次,如果利用后向引用简化,表达式如下:

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

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

    后向引用,引用的仅仅是文本内容,而不是正则表达式!

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

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

    不捕获

    不捕获就是在分组的前边加上?:,可以在不需要捕获分组的表达式中使用,加快表达式执行速度。

    就拿匹配<title>xxx</title>标签来说,通过分组可以简写为

    <(title)>.*</1> 

    但是如果是(?:title),则1就不能捕获到这个子组了,只能捕获第一个出现的非?:的分组作为1

    同时注意(?:title)本身会在完整匹配中,只是不在子组中,注意和断言的区别

    
    //正常,完整匹配为ab123ff, 有两个子组ab, ff
    /([a-z]{2}).*([a-z]{2})/.test(str)

    //不捕获分组,完整匹配为ab23ff, 有一个子组ff
    /(?:[a-z]{2}).*([a-z]{2})/.test(str)


    //断言,完整匹配为123ff, 有一个子组ff
    /(?<=[a-z]{2}).*([a-z]{2})/.test(str)

    断言

    所谓断言,就是指明某个字符串前边或者后边,将会出现满足某种规律的字符串。

    就拿匹配<title>xxx</title>标签来说,我们想要的是xxx,它没有规律,但是它前边肯定会有<title>,后边肯定会有</title>,这就足够了。

    想指定xxx前肯定会出现<title>,就用正后发断言,表达式:(?<=<title>).*

    想指定xxx后边肯定会出现</title>,就用正先行断言,表达式:.*(?=</title>)

    两个加在一起,就是(?<=<title>).*(?=</title>)

    这样就能匹配到xxx,匹配的内容不包括断言的内容,即完整的匹配就是xxx,不会包含<title>和</title>,子组中也不包含。

    对正后发和正先行的解释:

    其实掌握了规律,就很简单了,无论是先行还是后发,都是相对于xxx而言的,也就是相对于目标字符串而言。

    假如目标字符串后边有条件,可以理解为目标字符串在前,就用先行断言,放在目标字符串之后。

    假如目标字符串前边有条件,可以理解为目标字符串在后,就用后发断言,放在目标字符串之前。

    假如指定满足某个条件,就是正。

    假如指定不满足某个条件,就是负。

    断言只是条件,帮你找到真正需要的字符串,本身并不会匹配!

    !表示正好相反的意思,就是把=换成了!,看表格解释,X代表字符

    (?=X )
    零宽度正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。例如,w+(?=d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。
    (?!X)
    零宽度负先行断言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,w+(?!d) 与后不跟数字的单词匹配,而不与该数字匹配 。
    (?<=X)
    零宽度正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
    (?<!X)
    零宽度负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如,(?<!19)99 与不跟在 19 后面的 99 的实例匹配
     
     
  • 相关阅读:
    Shell脚本学习笔记2
    Shell脚本学习笔记1
    华大MCU硬件SMBus的应用
    华大MCU的应用中的问题记录
    STM32定时器输入捕获功能应用——超声波模块
    STM32定时器应用——PWM
    利用KEIL的软件仿真的逻辑分析仪功能观察GPIO的波形
    周期性过程数据通信和非周期性邮箱数据通信
    JDBCUtils
    反射
  • 原文地址:https://www.cnblogs.com/dfyg-xiaoxiao/p/12736235.html
Copyright © 2011-2022 走看看