匹配独特的群体,同时保持他们的秩序

Matching unique groups while maintaining their order

有没有一种方法可以完全在正则表达式中按出现顺序匹配唯一的字符组(在下面的例子中是单词)?如果是这样,该表达式与非正则表达式解决方案的效率相比如何?我正在使用 Python 的风格,但我也对任何其他风格的解决方案感兴趣。

这是一个示例案例:

string = 'the floodwaters are rising along the coast'
unique = ['the', 'floadwaters', 'are', 'rising', 'along', 'coast']

在 Python-regex 混合解决方案中,我可以匹配我想要的组,并使用列表理解来删除重复项,同时保持顺序。

groups = re.findall('[a-zA-Z]+', string)
unique = [g for i, g in enumerate(groups) if g not in groups[:i]]

整个站点都有类似的问题,例如one that addresses matching unique words。然而,接受的答案中的表达式匹配给定组的最右边出现,而我想匹配 first 出现。这是该表达式:

(\w+\b)(?!.*\b)

只有 无限宽度 后视才能实现这种任务的纯正则表达式解决方案。

但是,像这样的正则表达式解决方案应该考虑在输入比较短的情况下使用:超过100个单词由于回溯,在这种情况下,输入字符串将使其变得非常慢,这是不可避免的。因此,仅出于 学习目的 ,我将分享仅在 .NET 和 Python PyPi regex 库中支持的正则表达式(也可以这样做在 Vim 中,因为它的 lookbehind 也是无限宽度的,但我想这个强大的工具还有更简单的方法。

(?s)\b(\w+)\b(?<!^.*\b\b.*\b\b)

regex demo

(?s) 部分是一个内联修饰符,使 . 匹配所有换行符。您可以在 Python regex.

中使用 regex.DOTALL

详情

  • \b - 初始单词边界
  • (\w+) - 第 1 组:一个或多个单词字符
  • \b - 尾随单词边界
  • (?<!^.*\b\b.*\b\b) - 如果匹配到第 1 组的单词恰好在其自身之前至少出现一次,即如果紧接在当前位置的左侧(即在捕获的词之后),有一系列模式:
    • ^ - 字符串开头
    • .*\b\b - 任何零个或多个字符,尽可能多,然后与第 1 组中的值相同作为整个单词
    • .*\b\b - 与上面相同(需要匹配捕获的词,因为在 消耗的词之后使用 lookbehind)

lookbehind 中的 .* 会导致大量回溯,并且该模式通常会运行得相当慢,并且对于大量输入会非常慢,最终可能会导致超时。