1

次のJavaコードは、「abc」という単語をキャプチャすることを目的としていますが、代わりに「null」を返します。

Pattern p = Pattern.compile("^.*(\\ba\\w*\\b)?.*$");
Matcher m = p.matcher("xxx abc yyy");
if (m.matches()) System.out.println(m.group(1));

疑問符を削除すると、「abc」が正しくキャプチャされます。疑問符は貪欲なので、元のコードでも「abc」を指定する必要があると思いました。

理由を説明できる人に感謝します!

4

2 に答える 2

5

正規表現の.*最初のは貪欲なので、最初はできるだけ多くの文字(文字列全体)を一致させようとします。正規表現エンジンがキャプチャグループに移動する\ba\w*\bと、文字列の最後で一致できないことがわかりますが、グループはオプションであるため、バックトラックして一致を見つけようとはしません。

これを修正するに.*は、最初のをに変更します。これは、 .*?0個以上の文字と一致しますが、可能な限り一致しないようにします(貪欲ではなく怠惰)。

Pattern p = Pattern.compile("^.*?(\\ba\\w*\\b)?.*$");

もう1つの方法は、キャプチャグループの?後にあるものを削除して、キャプチャグループを必須にすることです。これにより、グループマッチが行われるまで、正規表現エンジンがバックトラックされます。ただし、これは正規表現の意味を変更するため、おそらくあなたが望むものではありません(一致する文字列が少なくなります)。

編集:私は本当にこれをテストすべきだったようです!.*結局のところ、に変更するだけでは.*?、グループは最初は一致せず、文字列全体が最後に一致するため.*(に変更しても.*?)、ここでは役に立ちません。

ここでの最善の策は?、グループが必要になるように、グループの後を削除することです。それでもすべての文字列を一致させたいが、グループと一致しない文字列に対してグループをnullにする場合は、次の正規表現を使用できます。

^(?:.*(\ba\w*\b).*|.*)$
于 2012-11-07T17:17:03.260 に答える
1

FJは原因について正しいです。


行で始まる最初のword-charシーケンスを明示的に一致aさせるには、単語以外の文字またはASCII文字以外の単語で始まる任意の数の単語を一致させa、オプションのキャプチャされたa単語の後に無視されたものを続けることができます。

abcこのプログラムは、期待どおりに印刷されます

import java.util.regex.*;

public class Foo {
  public static void main(String[] argv) {
    Pattern p = Pattern.compile("^(?:\\W|[b-zA-Z]\\w+)*(?:(a\\w*)?(?:.*))$");
    Matcher m = p.matcher("xxx abc yyy");
    if (m.matches()) System.out.println(m.group(1));
  }
}

正規表現は明確であるため、文字列に対して1回のフォワードパスのみが必要です。ただし、もっと注意深く読む必要があります。

私の傾向は、これらの状況は通常、明示的にトークン化することです-単語と非単語に分割してから、配列をループして必要なものを探します。


または、アンカーされていない正規表現のfind代わりに使用することもできます。match

find()パターンに一致する入力シーケンスの次のサブシーケンスを見つけようとします。

だからあなたはすることができます

Pattern p = Pattern.compile("(\\ba\\w*\\b)?");
Matcher m = p.matcher("xxx abc yyy")
while (m.find()) { System.out.println(m.group(1)); }

または、最初のものだけが必要な場合は、whileをに置き換えます。if


最後に、$Javaでの入力の終了を意味するものではありません。これは、入力の終了、または入力の終了時の改行の直前を意味します。javadocは、エンドアンカー間の微妙な違いを説明しています。

$
\Z の終わり入力の終わりですが、最後のターミネータ(存在する場合)の場合
\z 入力の終わり

于 2012-11-07T17:19:25.670 に答える