2009-06-12 4 views
63

J'ai ce code, et je veux savoir, si je peux remplacer seulement les groupes (pas tous les modèles) dans Java regex. code:Puis-je remplacer des groupes dans Java regex?

//... 
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

Pouvez-vous clarifier votre question, comme peut-être donner le résultat attendu pour cette entrée? –

Répondre

86

utilisation $n (où n est un chiffre) pour se référer à des sous-séquences capturées dans replaceFirst(...). Je suppose que vous vouliez remplacer le premier groupe par la chaîne littérale "numéro" et le deuxième groupe avec la valeur du premier groupe.

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 
} 

Tenir compte (\D+) pour le deuxième groupe au lieu de (.*). * est un matcher gourmand, et va d'abord consommer le dernier chiffre. Le matcher devra alors faire marche arrière lorsqu'il aura réalisé que le dernier (\d) n'a rien à faire, avant qu'il puisse correspondre au chiffre final.

+5

Aurait été bien si vous auriez posté un exemple de sortie – winklerrr

+5

Cela fonctionne sur le premier match, mais ne fonctionnera pas s'il y a beaucoup de groupes et vous itérez sur eux avec un certain temps (m.find()) –

+1

Je suis d'accord avec Hugo , c'est une façon terrible de mettre en œuvre la solution ... Pourquoi sur Terre est-ce la réponse acceptée et non la réponse d'acdcjunior - qui est la solution parfaite: petite quantité de code, haute cohésion et faible couplage, beaucoup moins de chance chance) d'effets secondaires indésirables ... * soupir * ... – Wrap2Win

8

Ajouter un troisième groupe en ajoutant parens autour .*, puis remplacez le par "number" + m.group(2) + "1"-séquence. par exemple .:

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

En fait, Matcher prend en charge le style de référence $ 2, donc m.replaceFirst ("number $ 21") ferait la même chose. –

+0

En fait, ils * ne * font pas la même chose. '" nombre $ 21 "' fonctionne et '" nombre "+ m.group (2) +" 1 "' ne fonctionne pas. –

+2

On dirait que le "nombre $ 21" remplacerait le groupe 21, pas le groupe 2 + la chaîne "1". –

1

Vous pouvez utiliser les méthodes matcher.start() et matcher.end() pour obtenir les positions de groupe. Donc, en utilisant ces positions, vous pouvez facilement remplacer n'importe quel texte.

33

Vous pouvez utiliser Matcher#start(group) et Matcher#end(group) pour construire une méthode de remplacement générique:

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

Vérifier online demo here.

+0

Ceci devrait vraiment être la réponse acceptée c'est la solution la plus complète et "prêt à aller" sans introduire un niveau de couplage au code d'accompagnement. Bien que je recommanderais de changer les noms de méthodes d'un de ceux-là. À première vue, cela ressemble à un appel récursif dans la première méthode. – Wrap2Win

+0

Opportunité d'édition manquée. Reprenez la partie sur l'appel récursif, n'a pas analysé le code correctement. Les surcharges fonctionnent bien ensemble – Wrap2Win

0

Voici une solution différente, qui permet également le remplacement d'un seul groupe dans plusieurs correspondances. Il utilise des piles pour inverser l'ordre d'exécution, de sorte que l'opération de chaîne peut être exécutée en toute sécurité.

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

Désolé de battre un cheval mort, mais il est de type de bizarre que faire-on fait remarquer ceci - « Oui, vous pouvez, mais cela est à l'opposé de la façon dont vous utilisez la capture des groupes dans la vie réelle ».

Si vous utilisez Regex la façon dont il est destiné à être utilisé, la solution est aussi simple que cela:

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

Vous n'utilisez généralement pas capture groupes sur les parties de la chaîne que vous voulez à ignorer, vous les utilisez sur la partie de la chaîne que vous voulez garder.

Si vous voulez vraiment des groupes que vous voulez remplacer, ce que vous voulez probablement un type de moteur de modèle (par exemple, moustache, express.js, StringTemplate, ...).

+0

Les groupes qui ne capturent pas sont inutiles; '\ d (. *) \ d' suffira. – shmosel