8

一意の一致のみを取得する方法はありますか?マッチング後にリストやマップを使用せずに、マッチャーの出力をすぐに一意にする必要があります。

サンプル入力/出力:

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]
4

1 に答える 1

17

はいあります。ネガティブルックアヘッドとバックリファレンスを組み合わせることができます。

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

これは、実際のパターンと一致したものが文字列内で再度発生しない場合にのみ一致します。事実上、これは常にすべての一致の最後の出現を取得することを意味するため、異なる順序でそれらを取得します。

[inputString]
[userName]

注文が問題になる場合(つまり、最初に注文することが重要な場合)、正規表現のみを使用してこれを行うことはできません。そのためには可変長のルック*ビハインド*が必要になりますが、これはJavaではサポートされていません。

参考文献:


一般的な解決策に関するいくつかの注意

これは、一致する幅がゼロ以外のすべてのパターンで機能することに注意してください。一般的な解決策は次のとおりです。

(yourPatternHere)(?!.*\1)

(これは一部の言語にのみ適用されるため、二重の円記号は省略しました。)

幅がゼロのパターンで機能させたい場合(位置を知りたいだけで、何らかの理由でルックアラウンドを使用しているため)、次のようにすることができます。

(zeroWidthPatternHere)(?!.+\1)

また、入力に改行が含まれている可能性がある場合は、(通常)「singleline」または「dotall」オプションを使用する必要がある場合があることに注意してください(そうでない場合、先読みは現在の行のみをチェックインします)。それをアクティブにできない、またはアクティブにしたくない場合(改行と一致してはならないピリオドを含むパターンがあるため、またはJavaScriptを使用しているため)、これが一般的な解決策です。

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

そして、この回答をさらに広く適用できるようにするために、すべての一致の最初の出現のみを一致させる方法を次に示します(.NETなどの可変長ルックビハインドを備えたエンジンで)。

(yourPatternHere)(?<!\1.*\1)
or
(yourPatternHere)(?<!\1[\s\S]*\1)
于 2012-11-28T20:30:01.763 に答える