web-dev-qa-db-fra.com

Remplacer le groupe 1 de regex Java sans remplacer l'intégralité de la regex

J'ai un motif de regex qui aura un seul groupe. Je dois trouver des textes dans les chaînes d'entrée qui suivent le modèle et remplacer UNIQUEMENT le groupe de correspondance 1. Par exemple, j'ai le modèle d'expression régulière et la chaîne à appliquer, comme indiqué ci-dessous. La chaîne de remplacement est "<---->"

Pattern p = Pattern.compile("\\w*(lan)\\w+");
Matcher m = p.matcher("plan plans lander planitia");

Le résultat attendu est 

plan p<--->s <--->der p<--->itia

J'ai essayé de suivre les approches

    String test = "plan plans lander planitia";
    Pattern p = Pattern.compile("\\w*(lan)\\w+");
    Matcher m = p.matcher(test);
    String result = "";
    while(m.find()){
        result = test.replaceAll(m.group(1),"<--->");
    }
    System.out.print(result);

Cela donne comme résultat 

p<---> p<--->s <--->der p<--->itia

Une autre approche

    String test = "plan plans lander planitia";
    Pattern p = Pattern.compile("\\w*(lan)\\w+");
    Matcher m = p.matcher(test);
    String result = "";
    while(m.find()){
        result = test.replaceAll("\\w*(lan)\\w+","<--->");
    }
    System.out.print(result);

Le résultat est

plan <---> <---> <--->

J'ai traversé this link. Ici la partie de la chaîne avant la correspondance est toujours constante et est "foo" mais dans mon cas, elle varie. J'ai aussi regardé ceci et ceci mais je suis incapable d'appliquer les solutions apportées à mon scénario actuel.

Toute aide est appréciée

7
Aditya

Vous devez utiliser le modèle suivant pour capturer des groupes:

(\w*)lan(\w+)
^-1-^   ^-2-^

et remplacez par $1<--->$2

Voir la démo regex ​​_

Le fait est que nous utilisons un groupe de capture autour des parties que nous voulons conserver et que nous ne faisons que correspondre à ce que nous voulons rejeter.

Démo Java :

String str = "plan plans lander planitia";
System.out.println(str.replaceAll("(\\w*)lan(\\w+)", "$1<--->$2"));
// => plan p<--->s <--->der p<--->itia

Si vous devez pouvoir remplacer le groupe 1 et conserver le reste, vous pouvez utiliser l'émulation de la méthode de rappel de remplacement avec Matcher#appendReplacement:

String text = "plan plans lander planitia";
String pattern = "\\w*(lan)\\w+";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(text);
StringBuffer sb = new StringBuffer();
while (m.find()) {
    m.appendReplacement(sb, m.group(0).replaceFirst(Pattern.quote(m.group(1)), "<--->"));
}
m.appendTail(sb); // append the rest of the contents
System.out.println(sb.toString());
// output => plan p<--->s <--->der p<--->itia

Voir une autre démo Java

Ici, puisque nous traitons une correspondance, nous ne devrions remplacer le contenu du groupe 1 qu'une seule fois par replaceFirst, et puisque nous remplaçons la sous-chaîne en tant que littéral, nous devrions Pattern.quote le.

18
Wiktor Stribiżew

Pour contrôler dynamiquement la valeur de remplacement, utilisez un find() boucle avec appendReplacement() , en finalisant le résultat avec appendTail() .

De cette façon, vous avez le plein contrôle de la valeur de remplacement. Dans votre cas, le motif est le suivant et vous pouvez obtenir les positions indiquées.

   start(1)
      ↓  end(1)
      ↓    ↓
  \\w*(lan)\\w+
  ↑            ↑
start()      end()

Vous pouvez ensuite extraire les valeurs à conserver.

String input = "plan plans lander planitia";

StringBuffer buf = new StringBuffer();
Matcher m = Pattern.compile("\\w*(lan)\\w+").matcher(input);
while (m.find())
    m.appendReplacement(buf, input.substring(m.start(), m.start(1)) +
                             "<--->" +
                             input.substring(m.end(1), m.end()));
String output = m.appendTail(buf).toString();

System.out.println(output);

Sortie

plan p<--->s <--->der p<--->itia

Si vous n'aimez pas le fait qu'il utilise la chaîne d'origine, vous pouvez utiliser la sous-chaîne correspondante à la place.

StringBuffer buf = new StringBuffer();
Matcher m = Pattern.compile("\\w*(lan)\\w+").matcher("plan plans lander planitia");
while (m.find()) {
    String match = m.group();
    int start = m.start();
    m.appendReplacement(buf, match.substring(0, m.start(1) - start) +
                             "<--->" +
                             match.substring(m.end(1) - start, m.end() - start));
}
String output = m.appendTail(buf).toString();
2
Andreas

Bien que l'explication de Wiktors sur l'utilisation des groupes de capture soit tout à fait correcte, vous pouvez éviter de les utiliser du tout. Le \\w* au début de votre modèle ne semble pas être pertinent, car vous souhaitez le conserver de toute façon, afin que nous puissions simplement le laisser en dehors du modèle. La vérification d'un caractère Word après lan peut être effectuée à l'aide d'un lookahead, tel que (?=\w), de sorte que nous ne correspondons que lan dans un modèle tel que "lan(?=\\w)" et que nous pouvons simplement remplacer par "<--->" (ou ce que vous voudrez).

1
Sebastian Proske

J'aime les autres solutions. Il s'agit d'une version à l'épreuve des balles légèrement optimisée:

public static void main (String [] args) {
    int groupPosition = 1;
    String replacement = "foo";
    Pattern r = Pattern.compile("foo(bar)");
    Matcher m = r.matcher("bar1234foobar1234bar");
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        StringBuffer buf = new StringBuffer(m.group());
        buf.replace(m.start(groupPosition)-m.start(), m.end(groupPosition)-m.start(), replacement); 
        m.appendReplacement(sb, buf.toString());
    }
    m.appendTail(sb); 
    System.out.println(sb.toString()); // result is "bar1234foofoo1234bar"
}
0
Ondřej Menčl