本网站上的更多内容 |
简介 |
正则表达式快速入门 |
正则表达式教程 |
替换字符串教程 |
应用程序和语言 |
正则表达式示例 |
正则表达式参考 |
替换字符串参考 |
书评 |
可打印 PDF |
关于本网站 |
RSS 提要和博客 |
环视在上一主题中进行了详细介绍,它是一个非常强大的概念。遗憾的是,初学者经常低估它,因为环视有点令人困惑。令人困惑的部分是环视是零长度的。因此,如果你有一个 regex,其中前瞻后跟另一段 regex,或者后顾前接另一段 regex,那么 regex 将遍历字符串的一部分两次。
一个更实际的示例可以说明这一点。假设我们想找到一个六个字母长且包含三个连续字母 cat的单词。实际上,我们可以在不环视的情况下匹配它。我们只需指定所有选项,并使用交替将它们组合在一起:cat\w{3}|\wcat\w{2}|\w{2}cat\w|\w{3}cat。足够简单。但是,如果你想找到一个长度在 6 到 12 个字母之间的单词,并且包含“cat”、“dog”或“mouse”,那么这种方法就会变得很笨拙。
在此示例中,我们基本上对成功匹配有两个要求。首先,我们想要一个长度为 6 个字母的单词。其次,我们找到的单词必须包含单词“cat”。
使用 \b\w{6}\b 匹配一个 6 个字母的单词很容易。匹配一个包含“cat”的单词同样容易:\b\w*cat\w*\b。
将两者结合起来,我们得到:(?=\b\w{6}\b)\b\w*cat\w*\b。很容易!以下是它的工作原理。在字符串中尝试正则表达式的每个字符位置,引擎首先尝试正则表达式中的正向肯定先行断言。此子正则表达式,因此正向肯定先行断言,仅在字符串中当前字符位置位于字符串中的 6 个字母单词开头时才匹配。如果不是,则正向肯定先行断言失败,引擎继续尝试从字符串中下一个字符位置开始的正则表达式。
正向肯定先行断言的长度为零。因此,当正向肯定先行断言中的正则表达式找到 6 个字母的单词时,字符串中的当前位置仍位于 6 个字母单词的开头。正则表达式引擎在此位置尝试其余的正则表达式。因为我们已经知道可以在当前位置匹配 6 个字母的单词,所以我们知道 \b 匹配,并且第一个 \w* 匹配 6 次。然后,引擎回溯,减少 \w* 匹配的字符数,直到可以匹配 cat。如果无法匹配 cat,则引擎别无选择,只能从正则表达式的开头重新开始,从字符串中的下一个字符位置开始。这在刚刚找到的 6 个字母单词的第二个字母处,正向肯定先行断言将失败,导致引擎逐个字符前进,直到下一个 6 个字母单词。
如果可以成功匹配 cat,则第二个 \w* 消耗 6 个字母单词中剩余的字母(如果有)。之后,正则表达式中的最后一个 \b 保证匹配正向肯定先行断言中的第二个 \b 匹配的位置。我们的双重要求正则表达式已成功匹配。
尽管上述正则表达式运行良好,但它并不是最优的解决方案。如果您只是在文本编辑器中进行搜索,这并不是问题。但是,如果您要重复使用此正则表达式和/或在您正在开发的应用程序中对大量数据进行操作,那么优化操作是一个好主意。
如果您仔细检查正则表达式并按照正则表达式引擎应用它的方式进行操作,您可以自己发现这些优化,就像我们上面所做的那样。第三个也是最后一个 \b 保证匹配。由于 词边界 为零长度,因此不会更改正则表达式引擎返回的结果,我们可以删除它们,留下: (?=\b\w{6}\b)\w*cat\w*。尽管最后一个 \w* 也保证匹配,但我们不能删除它,因为它会向正则表达式匹配中添加字符。请记住,先行断言会丢弃其匹配,因此它不会影响正则表达式引擎返回的匹配。如果我们省略 \w*,则结果匹配将是包含“cat”的 6 个字母单词的开头,直到“cat”为止,而不是整个单词。
但我们可以优化第一个 \w*。按原样,它将匹配 6 个字母,然后回溯。但我们知道,在成功的匹配中,“cat”之前最多只有 3 个字母。因此,我们可以将其优化为 \w{0,3}。请注意,使星号变为惰性并不能充分优化此操作。惰性星号会更快地找到成功的匹配,但如果一个 6 个字母的单词不包含“cat”,它仍然会导致正则表达式引擎尝试在最后两个字母、最后一个字母,甚至在 6 个字母单词之外的一个字符处匹配“cat”。
因此,我们有 (?=\b\w{6}\b)\w{0,3}cat\w*。最后一个次要优化涉及第一个 \b。由于其本身长度为零,因此无需将其放在前瞻中。因此,最终的正则表达式为:\b(?=\w{6}\b)\w{0,3}cat\w*。
你也可以用 \w{0,3} 替换最后的 \w*。但这不会产生任何差异。前瞻已经检查了我们是否处于一个 6 个字母的单词中,并且 \w{0,3}cat 已经匹配了该单词的 3 到 6 个字母。我们用 \w* 还是 \w{0,3} 结束正则表达式并不重要,因为无论哪种方式,我们都将匹配所有剩余的单词字符。由于匹配结果和找到匹配结果的速度相同,因此我们不妨使用更易于键入的版本。
那么,你将如何找到任何包含“cat”、“dog”或“mouse”的长度在 6 到 12 个字母之间的单词?同样,我们有两个要求,我们可以轻松地使用前瞻将它们组合起来:\b(?=\w{6,12}\b)\w{0,9}(cat|dog|mouse)\w*。一旦你掌握了诀窍,这非常容易。此正则表达式还会将“cat”、“dog”或“mouse”放入第一个反向引用中。
| 快速入门 | 教程 | 工具和语言 | 示例 | 参考 | 书籍评论 |
| 简介 | 目录 | 特殊字符 | 不可打印字符 | Regex 引擎内部 | 字符类 | 字符类减法 | 字符类交集 | 速记字符类 | 点 | 锚点 | 单词边界 | 交替 | 可选项 | 重复 | 分组和捕获 | 反向引用 | 反向引用,第 2 部分 | 命名组 | 相对反向引用 | 分支重置组 | 自由间距和注释 | Unicode | 模式修饰符 | 原子分组 | 占有量词 | 前瞻和后顾 | 前瞻和后顾,第 2 部分 | 将文本排除在匹配之外 | 条件 | 平衡组 | 递归 | 子例程 | 无限递归 | 递归和量词 | 递归和捕获 | 递归和反向引用 | 递归和回溯 | POSIX 方括号表达式 | 零长度匹配 | 继续匹配 |
页面网址:https://regexper.cn/lookaround2.html
页面上次更新时间:2024 年 2 月 7 日
网站上次更新时间:2024 年 3 月 15 日
版权所有 © 2003-2024 Jan Goyvaerts。保留所有权利。