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


问题内容

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

样本输入/输出:

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)"

只有在您的实际模式匹配的字符串中不再出现该字符串时,该字符串才会匹配。有效地,这意味着您总是获得每个匹配项的 最后一次
出现,因此您将以不同的顺序获得它们:

[inputString]
[userName]

如果订购对您来说是个问题(例如,对于首次订购时至关重要),您将无法仅使用正则表达式来完成。为此,您需要后面的可变长度外观,而Java不支持。

进一步阅读:


关于一般解决方案的一些注意事项

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

(yourPatternHere)(?!.*\1)

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

如果您希望它与宽度为零的模式一起使用(因为您只想知道某个位置,并且仅出于某种原因而使用环视),则可以执行以下操作:

(zeroWidthPatternHere)(?!.+\1)

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

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

为了使该答案更广泛地适用,这里是如何仅匹配每个匹配项的 一个匹配项(在具有可变长度后视功能的引擎(如.NET)中)的方法:

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