正则之环视
2016-06-25 09:36
183 查看
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*?wifi\S*wifi)\S{8,16}$
分隔数字
一串纯数字,从右开始每3位加逗号,如134545756变为134,545,756逆序肯定环视:
(?<=\d)(?=(?:\d\d\d)+$)
说明:
(?<=\d)是为了保证左侧第一个必须为数字
一串以$结尾的数字,从右开始每3位加逗号,如134545756$变为134,545,756$
逆序肯定环视:
(?<=\d)(?=(\d\d\d)+(?!\d))
需求:数字格式化成用“,”的货币格式。
**java8在逆序环视中,不支持无限字符数量的匹配量词,如*、+**
正则表达式:
(?<=\d)(?<!\.\d{0,100})(?=(?:\d{3})+(?:\.\d+|$))
标签
需求:匹配除<p…>或
</p>之外的其余标签,如:
aa<p>one</p>bb<div>two</div>cc
正则表达式:
<(?!/?p\b)[^>]+>
需求:匹配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\b)[^>]+>
这个正则的意义就是匹配除
<p…>或
</p>之外的其余标签。
![](https://images2015.cnblogs.com/blog/524483/201606/524483-20160611092106777-588624860.jpg)
匹配过程:
![](https://images2015.cnblogs.com/blog/524483/201606/524483-20160611092136761-1581188832.jpg)
首先由字符
<取得控制权,从位置0开始匹配,由于
<匹配
a失败,在位置0处整个表达式匹配失败,第一次迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。
重复以上过程,直到位置2,
<匹配
<成功,控制权交给
(?!/?p\b);
(?!/?p\b)子表达式取得控制权后,进行内部子表达式的匹配。首先由
/?取得控制权,尝试匹配
p失败,进行回溯,不匹配,控制权交给
p;由
p来尝试匹配
p,匹配成功,控制权交给
\b;由
\b来尝试匹配位置4,匹配成功。此时子表达式匹配完成,
/?p\b匹配成功,那么环视表达式
(?!/?p\b)就匹配失败。在位置2处整个表达式匹配失败,新一轮迭代匹配失败,正则引擎向前传动,由位置3处开始尝试下一轮迭代匹配。
在位置8处也会遇到一轮
/?p\b匹配
/p成功,而导致环视表达式
(?!/?p\b)匹配失败,从而导致整个表达式匹配失败的过程。
重复以上3过程,直到位置14,
<匹配
<成功,控制权交给
(?!/?p\b);
/?尝试匹配
d失败,进行回溯,不匹配,控制权交给
p;由
p来尝试匹配
d,匹配失败,已经没有备选状态可供回溯,匹配失败。此时子表达式匹配完成,
/?p\b匹配失败,那么环视表达式
(?!/?p\b)就匹配成功。匹配的结果是位置15,然后控制权交给
[^>]+;由
[^>]+从位置15进行尝试匹配,可以成功匹配到
div,控制权交给
>;由
>来匹配
>。
此时正则表达式匹配完成,报告匹配成功。匹配结果为
<div>,开始位置为14,结束位置为19。其中
<匹配
<,
(?!/?p\b)匹配位置15,
[^>]+匹配字符串
div,
>匹配
>。
逆序环视匹配过程
对于逆序肯定环视(?<=Expression)来说,当子表达式
Expression匹配成功时,
(?<=Expression)匹配成功,并报告
(?<=Expression)匹配当前位置成功。
对于逆序否定环视
(?<!Expression)来说,当子表达式
Expression匹配成功时,
(?<!Expression)匹配失败;当子表达式
Expression匹配失败时,
(?<!Expression)匹配成功,并报告
(?<!Expression)匹配当前位置成功;
顺序环视尝试匹配的起点是确定的,就是当前位置,而匹配的终点是不确定的。逆序环视匹配的起点是不确定的,是当前位置左侧某一位置,而匹配的终点是确定的,就是当前位置。
所以顺序环视相对是简单的,而逆序环视相对是复杂的。这也就是为什么大多数语言和工具都提供了对顺序环视的支持,而只有少数语言提供了对逆序环视支持的原因。
JavaScript中只支持顺序环视,不支持逆序环视。
Java中虽然顺序环视和逆序环视都支持,但是逆序环视只支持长度确定的表达式,逆序环视中量词只支持
?,不支持其它长度不定的量词。
目前只有.NET中支持不确定长度的逆序环视。
源字符串:
<div>a test</div>
正则表达式:
(?<=<div>)[^<]+(?=</div>)
这个正则的意义就是匹配和标签之间的内容,而不包括
<div>和
</div>标签本身。
匹配过程:
![](https://images2015.cnblogs.com/blog/524483/201606/524483-20160611092140636-1953000090.jpg)
首先由
(?<=<div>)取得控制权,从位置0开始匹配,由于位置0是起始位置,左侧没有任何内容,所以
<div>必然匹配失败,从而环视表达式
(?<=<div>)匹配失败,导致整个表达式在位置0处匹配失败。第一轮迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。
直到传动到位置5,
(?<=<div>)取得控制权,向左查找5个位置,由位置0开始匹配,由
<div>匹配
<div>成功,从而
(?<=<div>)匹配成功,匹配的结果为位置5,控制权交给
[^<]+;
[^<]+从位置5开始尝试匹配,匹配
a test成功,控制权交给
(?=</div>);由
</div>匹配
</div>成功,从而
(?=</div>)匹配成功,匹配结果为位置11。
此时正则表达式匹配完成,报告匹配成功。匹配结果为
a test,开始位置为5,结束位置为11。其中
(?<=<div>)匹配位置5,
[^<]+匹配
a test,
(?=</div>)匹配位置11。
逆序否定环视的匹配过程与上述过程类似,区别只是当Expression匹配失败时,逆序否定表达式(?<!Expression)才匹配成功。
4. 环视应用
需求:数字格式化成用,的货币格式。
正则表达式:
(?<=\d)(?<!\.\d*)(?=(?:\d{3})+(?:\.\d+|$))
实现分析:
首先根据需求可以确定是把一些特定的位置替换为“,”,接下来就是分析并找到这些位置的规律,并抽象出来以正则表达式来表示。
1、这个位置的左侧必须为数字
2、这个位置右侧到出现
.或结尾为止,必须是数字,且数字的个数必须为3的倍数
3、这个位置左侧相隔任意个数字不能出现
.
由以上三条,就可以完全确定这些位置,只要实现以上三条,组合一下正则表达式就可以了。
根据分析,最终匹配的结果是一个位置,所以所有子表达式都要求是零宽度。
1、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求必须出现,所以是肯定的,符合这一条件的子表达式即为
(?<=\d)
2、是对当前所在位置右侧附加的条件,所以要用到顺序环视,也是要求出现,所以是肯定的,是数字,且个数为3的倍数,即
(?=(?:\d{3})*),到出现
.或结尾为止,即
(?=(?:\d{3})*(?:\.|$))
3、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求不能出现,所以是否定的,即
(?<!\.\d*)
因为零宽度的子表达式是非互斥的,最后匹配的都是同一个位置,所以先后顺序是不影响最后的匹配结果的,可以任意组合,只是习惯上把逆序环视写在左侧,顺序环视写在右侧。
参考
相关文章推荐
- 使用android隐藏api实现亮度调节
- 学习小实例--滚动条的简单实现
- Java多线程并发锁和原子操作,你真的了解吗?
- [随记]在Hibernate中如何使用Blob数据类型
- Struts2的核心和工作原理
- 动态一维数组的使用
- Qt多线程之一:子线程中创建的对象不应再其他线程中被调用,包括使用槽函数的形式
- 如何玩好“用户思维”
- LineChar组件报错解决方法
- 3.hql查询实体:hibernate.cfg.xml
- 2.hql查询实体:工具类
- 4.hql简单属性查询:测试
- 4.hql查询实体:测试
- 1.hql查询实体:Object,hbm
- 5.hql简单属性查询:总结
- 2.hql简单属性查询:工具类
- 3.hql简单属性查询:hibernate.cfg.xml
- MySQL中Union子句不支持order by的解决方法
- 1.hql简单属性查询:Object,hbm
- 编写一个jsp程序,实现用户登录,当用户输入的用户或密码错误时,将页面重定向到错误提示页,并在该页面显示30秒后 自动回到用户登录界面