获得唯一的正则表达式匹配器结果(不使用地图或列表)

Posted

技术标签:

【中文标题】获得唯一的正则表达式匹配器结果(不使用地图或列表)【英文标题】:get unique regex matcher results (without using maps or lists) 【发布时间】:2012-11-16 19:40:54 【问题描述】:

有没有办法只获得唯一的匹配项?匹配后不使用列表或地图,我希望匹配器输出立即唯一。

示例输入/输出:

String input = "This is a question from [userName] about finding unique regex matches for [inputString] without using any lists or maps. -[userName].";
Pattern pattern = Pattern.compile("\\[[^\\[\\]]*\\]");
Matcher matcher = pattern.matcher(rawText);
while (matcher.find()) 
    String tokenName = matcher.group(0);
    System.out.println(tokenName);

这将输出以下内容:

[userName]
[inputString]
[userName]

但我希望它输出以下内容:

[userName]
[inputString]

【问题讨论】:

【参考方案1】:

是的,有。您可以将负前瞻和反向引用结合起来:

"(\\[[^\\[\\]]*\\])(?!.*\\1)"

只有当你的实际模式匹配的那个在字符串中不再出现时才会匹配。实际上,这意味着您总是得到每场比赛的最后次出现,因此您会以不同的顺序获得它们:

[inputString]
[userName]

如果订单对您来说是个问题(即,如果在第一次出现时对它们进行排序很重要),您将无法仅使用正则表达式来执行此操作。为此,您需要一个可变长度的look*behind*,而Java 不支持。

进一步阅读:

Lookarounds Backreferences

关于一般解决方案的一些说明

请注意,这适用于任何匹配宽度为非零的模式。一般的解决方案很简单:

(yourPatternHere)(?!.*\1)

(我省略了双反斜杠,因为它只适用于几种语言。)

如果您希望它与具有零宽度匹配的模式一起使用(因为您只想知道一个位置并且仅出于某种原因使用环视),您可以这样做:

(zeroWidthPatternHere)(?!.+\1)

另外,请注意(通常)您可能必须使用“singleline”或“dotall”选项,如果您的输入可能包含换行符(否则前瞻将只检查当前行)。如果您不能或不想激活它(因为您的模式包含不应匹配换行符的句点;或者因为您使用 javascript),这是一般的解决方案:

(yourPatternHere)(?![\s\S]*\1)

为了让这个答案更广泛地适用,这里是你如何只匹配每个匹配项的 first 出现(在具有可变长度后视的引擎中,如 .NET):

(yourPatternHere)(?<!\1.*\1)
or
(yourPatternHere)(?<!\1[\s\S]*\1)

【讨论】:

订单对我的需求并不重要,所以这是完美的。现在我只需要对前瞻和反向引用进行一些研究,以真正理解语法。干杯! @Ibrahim 我在这两个主题上添加了两个链接。 有很多人试图解释这个问题......但我只理解你的一般解释+1 @MartinEnder 感谢您的回复。它通过清晰的解释帮助我学习。 您好,我正在 Dreamweaver 正则表达式搜索和 Espresso 中尝试您的解决方案来测试它。但这对我不起作用。我试图在一个相当大的网站的源代码中找到所有不同的 datasource="someDSNname" 出现。但即使在一个小例子中,如果我出现两次,都会从正则表达式中返回。我试过了: (datasource=\"(.*?)\")(?!.\1) (datasource=\"(.*?)\")(?!.+\1)

以上是关于获得唯一的正则表达式匹配器结果(不使用地图或列表)的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式基础知识

Java正则表达式匹配器不匹配

Grep 不匹配正则表达式

正则表达式 整理

js中正则式匹配问题

如何使用模式匹配器仅获取与 Java 中正则表达式匹配的第一行?