2009-06-12 4 views
63

私はこのコードを持っていて、Java正規表現のグループ(すべてではない)だけを置き換えることができるかどうかを知りたいと思います。 コード:Java正規表現のグループを置き換えることはできますか?

//... 
Pattern p = Pattern.compile("(\\d).*(\\d)"); 
    String input = "6 example input 4"; 
    Matcher m = p.matcher(input); 
    if (m.find()) { 

     //Now I want replace group one ((\\d)) with number 
     //and group two (too (\\d)) with 1, but I don't know how. 

    } 
+4

あなたの質問を明確にすることができますか、その入力に予想される出力を与えることができますか? –

答えて

86

使用$nreplaceFirst(...)に捕捉サブシーケンスを参照する(Nは数字です)。私はあなたがリテラル文字列"番号"と最初のグループを置き換え、最初のグループの値を持つ2番目のグループにしたいと仮定しています。

Pattern p = Pattern.compile("(\\d)(.*)(\\d)"); 
String input = "6 example input 4"; 
Matcher m = p.matcher(input); 
if (m.find()) { 
    // replace first number with "number" and second number with the first 
    String output = m.replaceFirst("number $3$1"); // number 46 
} 

代わり(.*)の第二のグループのための(\D+)を考えます。 *は欲張りのマッチャーで、最初は最後の桁を消費します。最終的な数字に一致する前に、最終的な(\d)が一致するものがないことを認識すると、マッチャーはバックトラックする必要があります。

+5

出力例 – winklerrr

+5

を投稿した方がいいでしょうこれは最初の試合で動作しますが、多くのグループがあり、しばらくそれを繰り返している場合は動作しません –

+1

私はHugoと同意しますこれはソリューションを実装するためのひどい方法です...なぜ、地球上でこれは受け入れられた答えであり、acdcjuniorの答えではありません。これは完璧な解決策です:少量のコード、高結合力、低結合、偶然)不要な副作用... *一口* ... – Wrap2Win

8

が、その後"number" + m.group(2) + "1"でサブシーケンスを置き換える、.*の周りに括弧を追加することにより、第三のグループを追加します。例えば:

String output = m.replaceFirst("number" + m.group(2) + "1"); 
+4

実際、Matcherは$ 2スタイルのリファレンスをサポートしているので、m.replaceFirst( "number $ 21")は同じことをします。 –

+0

実際、彼らは同じ事をしません。 '' number $ 21 "'と '' number "+ m.group(2)+" 1 "'はありません。 –

+2

'number $ 21 'のようにグループ21を置き換え、グループ2 +文字列" 1 "のようには見えません。 –

1

matcher.start()およびmatcher.end()メソッドを使用して、グループの位置を取得できます。したがって、この位置を使用すると、テキストを簡単に置き換えることができます。

33

あなたは、一般的な交換方法を構築するためにMatcher#start(group)Matcher#end(group)を使用することができます。

public static String replaceGroup(String regex, String source, int groupToReplace, String replacement) { 
    return replaceGroup(regex, source, groupToReplace, 1, replacement); 
} 

public static String replaceGroup(String regex, String source, int groupToReplace, int groupOccurrence, String replacement) { 
    Matcher m = Pattern.compile(regex).matcher(source); 
    for (int i = 0; i < groupOccurrence; i++) 
     if (!m.find()) return source; // pattern not met, may also throw an exception here 
    return new StringBuilder(source).replace(m.start(groupToReplace), m.end(groupToReplace), replacement).toString(); 
} 

public static void main(String[] args) { 
    // replace with "%" what was matched by group 1 
    // input: aaa123ccc 
    // output: %123ccc 
    System.out.println(replaceGroup("([a-z]+)([0-9]+)([a-z]+)", "aaa123ccc", 1, "%")); 

    // replace with "!!!" what was matched the 4th time by the group 2 
    // input: a1b2c3d4e5 
    // output: a1b2c3d!!!e5 
    System.out.println(replaceGroup("([a-z])(\\d)", "a1b2c3d4e5", 2, 4, "!!!")); 
} 

チェックonline demo here

+0

これは実際には、導入されずに最も完全で「すぐに使える」解決策であるという、受け入れられた答えでなければなりません。これは、m.replaceFirst( "number $ 2 $ 1"); 'm.replaceFirst(" number $ 3 $ 1 "付随するコードへの結合のレベル。私はそれらのメソッド名を変更することをお勧めしますが。一見すると、最初のメソッドで再帰呼び出しのように見えます。 – Wrap2Win

+0

編集機会がありませんでした。再帰呼び出しについての部分を取り戻し、コードを適切に分析しなかった。オーバーロードがうまく機能します – Wrap2Win

0

ここでは、複数の一致で1つのグループを置き換えることができる別の解決法があります。 スタックを使用して実行順序を逆転させるので、文字列操作を安全に実行できます。 「はいすることができますが、これはあなたが本当の生活の中でキャプチャグループを使用する方法の反対である」 -

private static void demo() { 

    final String sourceString = "hello world!"; 

    final String regex = "(hello) (world)(!)"; 
    final Pattern pattern = Pattern.compile(regex); 

    String result = replaceTextOfMatchGroup(sourceString, pattern, 2, world -> world.toUpperCase()); 
    System.out.println(result); // output: hello WORLD! 
} 

public static String replaceTextOfMatchGroup(String sourceString, Pattern pattern, int groupToReplace, Function<String,String> replaceStrategy) { 
    Stack<Integer> startPositions = new Stack<>(); 
    Stack<Integer> endPositions = new Stack<>(); 
    Matcher matcher = pattern.matcher(sourceString); 

    while (matcher.find()) { 
     startPositions.push(matcher.start(groupToReplace)); 
     endPositions.push(matcher.end(groupToReplace)); 
    } 
    StringBuilder sb = new StringBuilder(sourceString); 
    while (! startPositions.isEmpty()) { 
     int start = startPositions.pop(); 
     int end = endPositions.pop(); 
     if (start >= 0 && end >= 0) { 
      sb.replace(start, end, replaceStrategy.apply(sourceString.substring(start, end))); 
     } 
    } 
    return sb.toString();  
} 
2

死んだ馬を打つために申し訳ありませんが、それはやる-1の種類-の奇妙であるが、これを指摘しました。

あなたが正規表現に使用されることを意味している方法を使用している場合は、解決策は、このように簡単です:

"6 example input 4".replaceAll("(?:\\d)(.*)(?:\\d)", "number$11"); 

あなたは通常、あなたがしたい文字列の部分にキャプチャグループを使用しないでください破棄には、としたい文字列の部分に使用します。

置換したいグループが本当に必要な場合は、テンプレートエンジン(例:口髭、express.js、StringTemplateなど)が必要なものを選択します。

+0

非キャプチャグループは不要です。 '\ d(。*)\ d 'で十分です。 – shmosel

関連する問題