zoukankan      html  css  js  c++  java
  • 正则之环视

    1.环视基础

    表达式 说明
    (?<=Expression) 逆序肯定环视,表示所在位置左侧能够匹配Expression
    (?<!Expression) 逆序否定环视,表示所在位置左侧不能匹配Expression
    (?=Expression) 顺序肯定环视,表示所在位置右侧能够匹配Expression
    (?!Expression) 顺序否定环视,表示所在位置右侧不能匹配Expression

    顺序环视相对是简单的,而逆序环视相对是复杂的

    • JavaScript中只支持顺序环视,不支持逆序环视。
    • Java中虽然顺序环视和逆序环视都支持,但是逆序环视只支持长度确定的表达式,逆序环视中量词只支持?,不支持其它长度不定的量词。

    2.示例

    至少包含

    密码由8到16位长度的英文字母、数字,且至少包含一个英文字母
    顺序肯定环视:^(?=[0-9]*+[a-zA-Z])[a-zA-Z0-9]{8,16}$
    密码由8到16位长度的英文字母、数字和特殊字符,且至少包含一个英文字母,一个数字,一个特殊字符,不能包含空格
    顺序肯定环视:^(?=[^a-zA-Z]*+[a-zA-Z])(?=[^0-9]*+[0-9])(?=[a-zA-Z0-9]*+[^a-zA-Z0-9])S{8,16}$

    不重复

    由字母、数字、特殊字符组合的8至16位长度,只能包含一次wifi
    顺序否定环视:^(?!S*?wifiS*wifi)S{8,16}$

    分隔数字

    一串纯数字,从右开始每3位加逗号,如134545756变为134,545,756
    逆序肯定环视:(?<=d)(?=(?:ddd)+$)
    说明:(?<=d)是为了保证左侧第一个必须为数字

    一串以$结尾的数字,从右开始每3位加逗号,如134545756$变为134,545,756$
    逆序肯定环视:(?<=d)(?=(ddd)+(?!d))

    需求:数字格式化成用“,”的货币格式。
    java8在逆序环视中,不支持无限字符数量的匹配量词,如*、+
    正则表达式:(?<=d)(?<!.d{0,100})(?=(?:d{3})+(?:.d+|$))

    标签

    需求:匹配除<p…></p>之外的其余标签,如:aa<p>one</p>bb<div>two</div>cc
    正则表达式:<(?!/?p)[^>]+>

    需求:匹配div标签之间的内容,如:<div>a test</div>
    正则表达式:(?<=<div>)[^<]+(?=</div>)

    3.环视匹配原理

    匹配位置

    顺序环视匹配过程

    • 对于顺序肯定环视(?=Expression)来说,当子表达式Expression匹配成功时,(?=Expression)匹配成功,并报告(?=Expression)匹配当前位置成功。
    • 对于顺序否定环视(?!Expression)来说,当子表达式Expression匹配成功时,(?!Expression)匹配失败;当子表达式Expression匹配失败时,(?!Expression)匹配成功,并报告(?!Expression)匹配当前位置成功。

    源字符串:aa<p>one</p>bb<div>two</div>cc
    正则表达式:<(?!/?p)[^>]+>
    这个正则的意义就是匹配除<p…></p>之外的其余标签。
    源字符串

    匹配过程:
    匹配过程

    1. 首先由字符<取得控制权,从位置0开始匹配,由于<匹配a失败,在位置0处整个表达式匹配失败,第一次迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。
    2. 重复以上过程,直到位置2,<匹配<成功,控制权交给(?!/?p)(?!/?p)子表达式取得控制权后,进行内部子表达式的匹配。首先由/?取得控制权,尝试匹配p失败,进行回溯,不匹配,控制权交给p;由p来尝试匹配p,匹配成功,控制权交给;由来尝试匹配位置4,匹配成功。此时子表达式匹配完成,/?p匹配成功,那么环视表达式(?!/?p)就匹配失败。在位置2处整个表达式匹配失败,新一轮迭代匹配失败,正则引擎向前传动,由位置3处开始尝试下一轮迭代匹配。
    3. 在位置8处也会遇到一轮/?p匹配/p成功,而导致环视表达式(?!/?p)匹配失败,从而导致整个表达式匹配失败的过程。
    4. 重复以上3过程,直到位置14,<匹配<成功,控制权交给(?!/?p)/?尝试匹配d失败,进行回溯,不匹配,控制权交给p;由p来尝试匹配d,匹配失败,已经没有备选状态可供回溯,匹配失败。此时子表达式匹配完成,/?p匹配失败,那么环视表达式(?!/?p)就匹配成功。匹配的结果是位置15,然后控制权交给[^>]+;由[^>]+从位置15进行尝试匹配,可以成功匹配到div,控制权交给>;由>来匹配>
      此时正则表达式匹配完成,报告匹配成功。匹配结果为<div>,开始位置为14,结束位置为19。其中<匹配<(?!/?p)匹配位置15,[^>]+匹配字符串div>匹配>

    逆序环视匹配过程

    • 对于逆序肯定环视(?<=Expression)来说,当子表达式Expression匹配成功时,(?<=Expression)匹配成功,并报告(?<=Expression)匹配当前位置成功。
    • 对于逆序否定环视(?<!Expression)来说,当子表达式Expression匹配成功时,(?<!Expression)匹配失败;当子表达式Expression匹配失败时,(?<!Expression)匹配成功,并报告(?<!Expression)匹配当前位置成功;
    • 顺序环视尝试匹配的起点是确定的,就是当前位置,而匹配的终点是不确定的。逆序环视匹配的起点是不确定的,是当前位置左侧某一位置,而匹配的终点是确定的,就是当前位置。
    • 所以顺序环视相对是简单的,而逆序环视相对是复杂的。这也就是为什么大多数语言和工具都提供了对顺序环视的支持,而只有少数语言提供了对逆序环视支持的原因。
    • JavaScript中只支持顺序环视,不支持逆序环视。
    • Java中虽然顺序环视和逆序环视都支持,但是逆序环视只支持长度确定的表达式,逆序环视中量词只支持?,不支持其它长度不定的量词。
    • 目前只有.NET中支持不确定长度的逆序环视。

    源字符串:<div>a test</div>
    正则表达式:(?<=<div>)[^<]+(?=</div>)
    这个正则的意义就是匹配

    标签之间的内容,而不包括<div></div>标签本身。

    匹配过程:
    匹配过程

    1. 首先由(?<=<div>)取得控制权,从位置0开始匹配,由于位置0是起始位置,左侧没有任何内容,所以<div>必然匹配失败,从而环视表达式(?<=<div>)匹配失败,导致整个表达式在位置0处匹配失败。第一轮迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。
    2. 直到传动到位置5,(?<=<div>)取得控制权,向左查找5个位置,由位置0开始匹配,由<div>匹配<div>成功,从而(?<=<div>)匹配成功,匹配的结果为位置5,控制权交给[^<]+[^<]+从位置5开始尝试匹配,匹配a test成功,控制权交给(?=</div>);由</div>匹配</div>成功,从而(?=</div>)匹配成功,匹配结果为位置11。
    3. 此时正则表达式匹配完成,报告匹配成功。匹配结果为a test,开始位置为5,结束位置为11。其中(?<=<div>)匹配位置5,[^<]+匹配a test(?=</div>)匹配位置11。
    4. 逆序否定环视的匹配过程与上述过程类似,区别只是当Expression匹配失败时,逆序否定表达式(?<!Expression)才匹配成功。

    4. 环视应用

    需求:数字格式化成用,的货币格式。
    正则表达式:(?<=d)(?<!.d*)(?=(?:d{3})+(?:.d+|$))

    实现分析:
    首先根据需求可以确定是把一些特定的位置替换为“,”,接下来就是分析并找到这些位置的规律,并抽象出来以正则表达式来表示。
    1、这个位置的左侧必须为数字
    2、这个位置右侧到出现.或结尾为止,必须是数字,且数字的个数必须为3的倍数
    3、这个位置左侧相隔任意个数字不能出现.

    由以上三条,就可以完全确定这些位置,只要实现以上三条,组合一下正则表达式就可以了。
    根据分析,最终匹配的结果是一个位置,所以所有子表达式都要求是零宽度。
    1、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求必须出现,所以是肯定的,符合这一条件的子表达式即为(?<=d)
    2、是对当前所在位置右侧附加的条件,所以要用到顺序环视,也是要求出现,所以是肯定的,是数字,且个数为3的倍数,即(?=(?:d{3})*),到出现.或结尾为止,即(?=(?:d{3})*(?:.|$))
    3、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求不能出现,所以是否定的,即(?<!.d*)

    因为零宽度的子表达式是非互斥的,最后匹配的都是同一个位置,所以先后顺序是不影响最后的匹配结果的,可以任意组合,只是习惯上把逆序环视写在左侧,顺序环视写在右侧。

    参考

    正则基础之 环视 Lookaround
    正则应用之 逆序环视探索
    正则匹配原理之 逆序环视深入

  • 相关阅读:
    Tomcatd断点调试Debug
    idea怎么部署Servlet
    ECMAScript基本语法——①与HTML的结合方式
    JavaScript简介
    程序员找工作,应该怎么应对面试官?
    你所未知的3种 Node.js 代码优化方式
    对 APM 用户的一次真实调查分析(上)
    Datadog Agent是啥?它消耗什么资源?
    Python 全栈开发 -- 开发环境篇
    成为运维界的「福尔摩斯」,你还需要3个帮手!
  • 原文地址:https://www.cnblogs.com/chencye/p/5615839.html
Copyright © 2011-2022 走看看