匹配位置型元字符
环视类型元字符也叫断言,比如:
- 正序肯定环视
==
正序肯定断言==
肯定正序断言; - 逆序肯定环视
==
逆序肯定断言==
肯定逆序断言。
元字符 | 中文名称 | 匹配对象 |
---|---|---|
^ | 脱字符 | 匹配一行起始位置 |
$ | 美元符 | 匹配一行结束位置 |
\b | 单词边界符 | 匹配单词边界,即单词与空格之间的位置 |
\B | 非单词边界符 | 匹配非单词的边界,即单词与单词之间的某位置 |
(?=char) | 肯定正序环视 | 自左向右查找文本,匹配一个位置,该位置的右侧满足 子表达式(即char ) |
(?<=char) | 肯定逆序环视 | 自右向左查找文本,匹配一个位置,该位置的左侧满足 子表达式(即char ) |
(?!char) | 否定正序环视 | 自左向右查找文本,匹配一个位置,该位置的右侧不满足 子表达式(即char ) |
(?<!char) | 否定逆序环视 | 自右向左查找文本,匹配一个位置,该位置的左侧不满足 子表达式(即char ) |
注意
char
为要匹配的字符串或表达式;- 通常匹配位置型元字符只作为某一表达式的一部分(即子表达式)来使用,以提高匹配精度;
- 脱字符
^
在字符组内部时将被正则引擎视为逻辑非
,即取反; - 单词边界
\b
在字符组内部是表示匹配一个退格(U+0008
); - 环视(断言)结构
均不会
“占用”文本,只匹配一个位置。
语法声明
本页示例均使用的JavaScript
正则语法声明 const regexp = /pattern/flags
:
脱字符 ^
假设现在的需求为:使用忽略大小的全局匹配
,只想匹配行开始处的AchooLuv
,不匹配achooluv
和行尾的Achooluv
const str = "AchooLuv and achooluv and Achooluv"; // 注意大小写
const regexp = /AchooLuv/gi; // 标识符g表示进行全局匹配
const ret = str.match(regexp);
console.log(ret); // 匹配结果为: ['AchooLuv', 'achooluv', 'Achooluv']
很明显这是把achooluv
和结尾处的Achooluv
也成功匹配了,并非我们想要的结果。
稍稍改造,使用^
让结果更准确
const str = "AchooLuv and achooluv and Achooluv";
const regexp = /^AchooLuv/gi;
const ret = str.match(regexp);
console.log(ret); // 匹配结果为: ['AchooLuv']
此时的结果才是符合我们预期的匹配结果。
美元符 $
现在我们改下需求:依然使用忽略大小的全局匹配
,但是现在只想要匹配行尾的Achooluv
,不匹配行开始处的AchooLuv
和中间的achooluv
使用$
完成需求
const str = "AchooLuv and achooluv and Achooluv"; // 注意大小写
const regexp = /AchooLuv$/gi;
const ret = str.match(regexp);
console.log(ret); // 匹配结果为: ['Achooluv']
匹配的结果刚好是我们需求中的子串。
单词边界符 \b
假设现在有如下需求:我们需要从字符串Design by AchooLuv, not Achoo
,匹配完整的单词Achoo
,而不匹配AchooLuv
const str = "Design by AchooLuv, not Achoo";
const regexp = /Achoo/;
const ret = str.match(regexp);
console.log(ret); // 匹配结果为: ['Achoo',index: 10]
为了更直观的看出匹配结果来自哪里,此处未使用全局匹配标识符g
(若使用全局标识符g
或者String#matchAll()
,返回均可视为['Achoo','Achoo'],区别在于String#matchAll()
返回的是可迭代对象)。
此时的index: 10
很明显告诉我们匹配结果来自AchooLuv
,而非完整单词Achoo
,那怎样才能达到需求中的匹配结果呢?
使用\b
匹配单词边界这一位置,来提高匹配的精确度
const str = "Design by AchooLuv, not Achoo";
const regexp = /\bAchoo\b/;
const ret = str.match(regexp);
console.log(ret); // 匹配结果为: ['Achoo',index: 24]
而此时的index: 24
告诉我们匹配结果来自完整的单词Achoo
,而非AchooLuv
,刚好是我们需求中的内容。
非单词边界符 \B
我们将字符串改为Design by Achoo, not AchooLuv
,现在需求变为匹配内容来仅自AchooLuv
,而非完整单词Achoo
。
由上面的例子我们可以知道,不使用匹配位置元字符,匹配的结果不可能仅来自AchooLuv
,此时又该如何完成需求呢?
使用\B
匹配非单词边界这一位置
const str = "Design by Achoo, not AchooLuv";
const regexp = /\bAchoo\B/;
const ret = str.match(regexp);
console.log(ret); // 匹配结果为: ['Achoo',index: 21]
匹配结果中index
的值是期望中的21
(来自AchooLuv
),而非10
(来自完整单词Achoo
)。
肯定正序环视 (?=)
现在有如下需求:在字符串Design by AchooLuv
的AchooLuv
中添加-
,使其变为Design by Achoo-Luv
,如何完成这一需求呢?
不难思考,我们需要匹配一个位置,一个什么样的位置?满足以下条件:
- 该位置的左侧
可以
是非单词边界即:\B
; - 该位置的右侧
可以
是luv\b
。
综上我们便可以写出正则表达式子串:\Bluv\b
,需要满足位置的条件确定,完成正则表达式并测试:
const str = "Design by AchooLuv";
const regexp = /(?=\BLuv\b)/;
const ret = str.replace(regexp, "-");
console.log(ret); // 返回结果为:Design by Achoo-Luv
完美通过测试,完成需求。
肯定逆序环视 (?<=)
注意
肯定逆序环视是ES9
才正式支持的正则新特性,使用时需要注意浏览器支持情况!!!
相同的需求能用肯定逆序环视(?<=)
完成吗?
同样的我们依然需要匹配一个位置,该位置满足以下条件:
- 该位置的右侧
可以
是非单词边界即:\B
; - 该位置的左侧
可以
是\bAchoo
。
同理,综上我们便可以写出正则表达式子串:\bAchoo\B
,现在测试:
const str = "Design by AchooLuv";
const regexp = /(?<=\bAchoo\B)/;
const ret = str.replace(regexp, "-");
console.log(ret); // 返回结果为:Design by Achoo-Luv
返回结果显示,使用肯定逆序环视(?<=)
也能完成这一需求。
能同时使用(?=)
和(?<=)
来完成同一位置的匹配吗?
const str = "Design by AchooLuv";
const regexp = /(?<=\bAchoo\B)(?=\BLuv\b)/;
const ret = str.replace(regexp, "-");
console.log(ret); // 返回结果为:Design by Achoo-Luv
返回结果可知,我们完成了需求,也说明是可以同时使用来提高匹配精度的。
否定正序环视 (?!)
假设现有需求如下:将字符串Design by AchooLuv, not AchooNya
中的单词AchooNya
修改为Achoo-Nya
,如何完成这一需求你?
通过前面的学习你肯定马上就想到了(?=)
和(?<=)
,是滴,它们的确可以完成我们的需求,但是现在要拓宽我们的处理方法,使用否定正序环视(?!)
:
- 首先使用肯定逆序环视
(?<=\bachoo\B)
将匹配对象限制在AchooLuv
和AchooNya
中; - 接着再使用否定正序环视
(?!\BLuv\b)
匹配Achoo
后紧接着不是Luv
的AchooNya
中的正确位置。
现在测试我们的正则表达式:
const str = "Design by AchooLuv, not AchooNya";
const regexp = /(?<=\bAchoo\B)(?!\BLuv\b)/;
const ret = str.replace(regexp, "-");
console.log(ret); // 返回结果为:Design by AchooLuv, not Achoo-Nya
由返回结果可知,完美完成需求。
否定逆序环视 (?<!)
注意
否定逆序环视是ES9
才正式支持的正则新特性,使用时需要注意浏览器支持情况!!!
现在我们将上诉需求改为:将字符串Design by AchooLuv, not AkoLuv
中的单词AkoLuv
修改为Ako-Luv
。
为了完成这一需求:
- 首先使用肯定正序环视
(?=\BLuv\b)
将匹配对象限制在AchooLuv
和AkoLuv
中; - 接着再使用否定逆序环视
(?<!\bachoo\B)
匹配Luv
前紧挨着不是Achoo
的AkoLuv
中的正确位置。
const str = "Design by AchooLuv, not AkoLuv";
const regexp = /(?<!\bAchoo\B)(?=\BLuv\b)/;
const ret = str.replace(regexp, "-");
console.log(ret); // 返回结果为:Design by AchooLuv, not Ako-Luv
同样也完成了需求中匹配。