添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
2022年10月30日

前瞻断言与后瞻断言

有时我们只需要为一个模式找到那些在另一个模式之后或之前的匹配项。

有一种特殊的语法,称为“前瞻断言(lookahead)”和“后瞻断言(lookbehind)”。

首先,让我们从字符串中查找价格,例如 1 turkey costs 30€ 。即:一个数字,后跟 符号。

前瞻断言

语法为: x(?=y) ,它表示“仅在后面是 Y 时匹配 X ”。这里的 X Y 可以是任何模式。

那么对于一个后面跟着 的整数,正则表达式应该为: \d+(?=€)

请注意:前瞻断言只是一个测试,括号 (?=...) 中的内容不包含在匹配结果 30 中。

当我们查找 X(?=Y) 时,正则表达式引擎会找到 X ,然后检查其后是否有 Y 。如果没有,则跳过潜在匹配,并继续搜索。

更复杂的测试也是可能的,例如 X(?=Y)(?=Z) 表示:

  • 寻找 X
  • 检查 Y 是否紧跟在 X 之后(如果不是则跳过)。
  • 检查 Z 是否也在 X 之后(如果不是则跳过)。
  • 如果两个测试都通过了,那么 X 是匹配的,否则继续搜索。
  • 换句话说,这样的模式意味着我们同时在寻找 X 后跟 Y Z

    这只有在模式 Y Z 不是互斥的情况下才可行。

    例如, \d+(?=\s)(?=.*30) 查找后跟着空格 (?=\s) \d+ ,并且有 30 在它之后的某个地方 (?=.*30)

    在我们给出的字符串中,与数字 1 完全匹配。

    否定的前瞻断言

    假设我们想要一个数量,而不是来自同一字符串的价格。那是一个数字 \d+ ,后面不是

    为此,我们可以使用否定的前瞻断言。

    语法是: X(?!Y) ,意思是“搜索 X ,但前提是后面没有 Y ”。

    捕获组

    一般来说,前瞻断言和后瞻断言括号中的内容不会成为结果的一部分。

    例如,在模式 \d+(?!€) 中, 符号就不会出现在匹配结果中。这是很自然的事:我们寻找一个数字 \d+ ,而 (?=€) 只是一个测试,表示要匹配的数字后面应该紧跟着 字符。

    但在某些情况下,我们可能还想捕获前瞻断言和后瞻断言所匹配的内容,或者部分内容。这也是可行的。只需要将该部分包装在额外的括号中。

    在下面的示例中,货币符号 (€|kr) 和金额一起被捕获了:

    总结

    当我们想根据前面/后面的上下文匹配某些内容的时候,前瞻断言和后瞻断言(通常被称为“环视断言”)很有用。

    对于简单的正则表达式,我们可以手动执行类似的操作。即:不管上下文,匹配所有可匹配的内容,然后在循环中根据上下文进行过滤。

    请记住, str.match (没有修饰符 g )和 str.matchAll (总是)将匹配项作为具有 index 属性的数组返回,因此我们知道它在文本中的确切位置,并且可以检查上下文。

    但通常环视断言更方便。

    环视断言类型:

    正如我们所看到的,它从 -18 中配到了 8 。要排除这种情况,我们需要确保正则表达式要从一个数的开头开始匹配数字,而不是从另一个(不匹配的)数字的中间开始进行匹配。

    我们可以通过指定另一个否定的后瞻断言来实现这一点: (?<!-)(?<!\d)\d+ 。现在 (?<!\d) 确保匹配不会从另一个数字之后开始进行匹配了,这正是我们所需要的。

    我们也可以把它们合并成一个后瞻断言:

    为了在 <body> 标签后面插入内容,我们必须先找到它。我们可以使用正则表达式 <body.*?> 来实现。

    在本任务中,我们不需要修改 <body> 标签。我们只需要在它后面添加文本。

    我们可以这样做:

    let str = '...<body style="...">...';
    str = str.replace(/(?<=<body.*?>)/, `<h1>Hello</h1>`);
    alert(str); // ...<body style="..."><h1>Hello</h1>...

    正如你所看到的,这个正则表达式中只有后瞻断言部分。

    它的工作原理如下:

  • 在文本的每个位置。
  • 检查它前面是否有 <body.*?>
  •