Skip to content

匹配位置型元字符

环视类型元字符也叫断言,比如:

  • 正序肯定环视==正序肯定断言==肯定正序断言;
  • 逆序肯定环视==逆序肯定断言==肯定逆序断言。
元字符
中文名称
匹配对象
^脱字符匹配一行起始位置
$美元符匹配一行结束位置
\b单词边界符匹配单词边界,即单词与空格之间的位置
\B非单词边界符匹配非单词的边界,即单词与单词之间的某位置
(?=char)肯定正序环视自左向右查找文本,匹配一个位置,该位置的右侧满足子表达式(即char)
(?<=char)肯定逆序环视自右向左查找文本,匹配一个位置,该位置的左侧满足子表达式(即char)
(?!char)否定正序环视自左向右查找文本,匹配一个位置,该位置的右侧不满足子表达式(即char)
(?<!char)否定逆序环视自右向左查找文本,匹配一个位置,该位置的左侧不满足子表达式(即char)

注意

  • char为要匹配的字符串或表达式;
  • 通常匹配位置型元字符只作为某一表达式的一部分(即子表达式)来使用,以提高匹配精度;
  • 脱字符^在字符组内部时将被正则引擎视为逻辑非,即取反;
  • 单词边界\b在字符组内部是表示匹配一个退格(U+0008);
  • 环视(断言)结构均不会“占用”文本,只匹配一个位置。

语法声明

本页示例均使用的JavaScript正则语法声明 const regexp = /pattern/flags:

脱字符 ^

假设现在的需求为:使用忽略大小的全局匹配,只想匹配行开始处的AchooLuv,不匹配achooluv和行尾的Achooluv

javascript
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也成功匹配了,并非我们想要的结果。

稍稍改造,使用^让结果更准确

javascript
const str = "AchooLuv and achooluv and Achooluv";
const regexp = /^AchooLuv/gi;
const ret = str.match(regexp);
console.log(ret); // 匹配结果为: ['AchooLuv']

此时的结果才是符合我们预期的匹配结果。

美元符 $

现在我们改下需求:依然使用忽略大小的全局匹配,但是现在只想要匹配行尾的Achooluv,不匹配行开始处的AchooLuv和中间的achooluv

使用$完成需求

javascript
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

javascript
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匹配单词边界这一位置,来提高匹配的精确度

javascript
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匹配非单词边界这一位置

javascript
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 AchooLuvAchooLuv中添加-,使其变为Design by Achoo-Luv,如何完成这一需求呢?
不难思考,我们需要匹配一个位置,一个什么样的位置?满足以下条件:

  1. 该位置的左侧可以是非单词边界即:\B;
  2. 该位置的右侧可以luv\b

综上我们便可以写出正则表达式子串:\Bluv\b,需要满足位置的条件确定,完成正则表达式并测试:

javascript
const str = "Design by AchooLuv";
const regexp = /(?=\BLuv\b)/;
const ret = str.replace(regexp, "-");
console.log(ret); // 返回结果为:Design by Achoo-Luv

完美通过测试,完成需求。

肯定逆序环视 (?<=)

注意

肯定逆序环视是ES9才正式支持的正则新特性,使用时需要注意浏览器支持情况!!!

相同的需求能用肯定逆序环视(?<=)完成吗?
同样的我们依然需要匹配一个位置,该位置满足以下条件:

  1. 该位置的右侧可以是非单词边界即:\B;
  2. 该位置的左侧可以\bAchoo

同理,综上我们便可以写出正则表达式子串:\bAchoo\B,现在测试:

javascript
const str = "Design by AchooLuv";
const regexp = /(?<=\bAchoo\B)/;
const ret = str.replace(regexp, "-");
console.log(ret); // 返回结果为:Design by Achoo-Luv

返回结果显示,使用肯定逆序环视(?<=)也能完成这一需求。

能同时使用(?=)(?<=)来完成同一位置的匹配吗?

javascript
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,如何完成这一需求你?
通过前面的学习你肯定马上就想到了(?=)(?<=),是滴,它们的确可以完成我们的需求,但是现在要拓宽我们的处理方法,使用否定正序环视(?!)

  1. 首先使用肯定逆序环视(?<=\bachoo\B)将匹配对象限制在AchooLuvAchooNya中;
  2. 接着再使用否定正序环视(?!\BLuv\b)匹配Achoo后紧接着不是LuvAchooNya中的正确位置。

现在测试我们的正则表达式:

javascript
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
为了完成这一需求:

  1. 首先使用肯定正序环视(?=\BLuv\b)将匹配对象限制在AchooLuvAkoLuv中;
  2. 接着再使用否定逆序环视(?<!\bachoo\B)匹配Luv前紧挨着不是AchooAkoLuv中的正确位置。
javascript
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

同样也完成了需求中匹配。