2009-06-12 9 views
63

Mam ten kod i chcę wiedzieć, czy mogę zastąpić tylko grupy (nie wszystkie wzorce) w regex Java. Kod:Czy mogę zastąpić grupy w regex 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

Czy możesz wyjaśnić swoje pytanie, jak może dać oczekiwany wynik dla tego wejścia? –

Odpowiedz

86

Zastosowanie $n (gdzie n oznacza cyfrę) odnosi się do przechwyconych podsekwencji w replaceFirst(...). Zakładam, że chciałeś zastąpić pierwszą grupę literalnym ciągiem "number" i drugą grupą z wartością pierwszej grupy.

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 
} 

Rozważmy (\D+) dla drugiej grupy zamiast (.*). * jest chciwym matcherem i na początku zużyje ostatnią cyfrę. Matcher będzie musiał następnie wycofać się, gdy zorientuje się, że ostateczny (\d) nie ma nic do dopasowania, zanim dopasuje się do ostatniej cyfry.

+5

Byłoby miło, gdybyś opublikował przykładowy wynik – winklerrr

+5

Działa to w pierwszym meczu, ale przydaje się, jeśli jest wiele grup i iterujesz nad nimi przez jakiś czas (m.find()) –

+1

Zgadzam się z Hugo To jest okropny sposób na wdrożenie rozwiązania ... Dlaczego na Ziemi jest to zaakceptowana odpowiedź, a nie odpowiedź acdcjunior - która jest idealnym rozwiązaniem: mała ilość kodu, wysoka spójność i niskie sprzężenie, znacznie mniejsza szansa (jeśli nie szansa) niechcianych efektów ubocznych ... * westchnienie * ... – Wrap2Win

8

Dodaj trzecią grupę, dodając parens wokół .*, a następnie zastąpić podciąg z "number" + m.group(2) + "1". np .:

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

W rzeczywistości Matcher obsługuje styl odwołań w wysokości 2 $, więc m.replaceFirst ("numer $ 21") zrobiłby to samo. –

+0

W rzeczywistości oni * nie * robią to samo. '' number $ 21 "' works i '" number "+ m.group (2) +" 1 "' nie. –

+2

Wygląda na to, że 'liczba $ 21' zastąpiłaby grupę 21, a nie grupę 2 + łańcuch" 1 ". –

1

Możesz użyć metod matcher.start() i matcher.end(), aby uzyskać pozycje grup. Używając tych pozycji, możesz łatwo zamienić dowolny tekst.

33

Można użyć Matcher#start(group) i Matcher#end(group) zbudować ogólną metodę zastępczą:

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, "!!!")); 
} 

Sprawdź online demo here.

+0

To naprawdę powinna być zaakceptowana odpowiedź, jest to najbardziej kompletne i "gotowe do użycia" rozwiązanie bez wprowadzania poziom sprzężenia z towarzyszącym kodem. Chociaż zalecałbym zmianę nazw metod jednego z nich. Na pierwszy rzut oka wygląda to jak wywołanie rekurencyjne w pierwszej metodzie. – Wrap2Win

+0

Nieodebrane miejsce do edycji. Odzyskaj część dotyczącą wywołania rekurencyjnego, nie analizuj poprawnie kodu. Przeciążenia dobrze się sprawdzają – Wrap2Win

0

Oto inne rozwiązanie, które pozwala również na zastąpienie pojedynczej grupy w wielu meczach. Używa stosów do odwrócenia kolejności wykonania, aby operacja na łańcuchach mogła być bezpiecznie wykonana.

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

Niestety pokonać martwego konia, ale jest to rodzaj-dziwne, że zrobić jeden wskazywał na to - „Tak można, ale to jest przeciwieństwem sposobu korzystania przechwytywanie grup w prawdziwym życiu”.

Jeśli używasz regex sposób, w jaki ma być stosowany, rozwiązanie jest tak proste, jak to:

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

Zwykle nie używać przechwytywanie grupy na części napisu chcesz do odrzuć, używasz ich na części łańcucha, który chcesz zachować.

Jeśli naprawdę chcesz grupy, które chcesz zastąpić, prawdopodobnie chcesz mieć mechanizm szablonowy (np. Wąsy, express.js, StringTemplate, ...).

+0

Grupy niezapisujące są niepotrzebne; '\ d (. *) \ d' wystarczy. – shmosel