web-dev-qa-db-fra.com

Puis-je remplacer des groupes dans Java regex?

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

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

    }
77
wokena

Utilisez $n (où n est un chiffre) pour faire référence aux sous-séquences capturées dans replaceFirst(...) . Je suppose que vous vouliez remplacer le premier groupe par la chaîne littérale "nombre" et le deuxième groupe par 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
}

Considérons (\D+) pour le deuxième groupe au lieu de (.*). * est un matcher gourmand, et utilisera d'abord le dernier chiffre. Le matcher devra alors revenir en arrière quand il réalisera que le (\d) final n'a rien à faire avant de pouvoir correspondre au dernier chiffre.

102
Chadwick

Vous pouvez utiliser Matcher#start(group) et Matcher#end(group) pour créer 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érifiez la démo en ligne ici.

44
acdcjunior

Ajoutez un troisième groupe en ajoutant des parenthèses autour de .*, puis remplacez la sous-séquence par "number" + m.group(2) + "1". par exemple.:

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

Désolé de battre un cheval mort, mais il est un peu bizarre que personne ne le souligne - "Oui, vous le pouvez, mais c'est le contraire de la façon dont vous utilisez des groupes de capture dans la vie réelle".

Si vous utilisez Regex comme il est censé être utilisé, la solution est aussi simple que cela:

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

Ou comme l'a justement souligné par Shmosel ci-dessous,

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

... car dans votre regex, il n'y a aucune bonne raison de regrouper les décimales. 

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

Si vous souhaitez réellement remplacer des groupes, vous voudrez probablement un moteur de modélisation (par exemple, Moustache, Ejs, StringTemplate, ...).


Pour les curieux, même dans les expressions rationnelles, même les groupes qui ne capturent pas ne sont là que pour le cas où le moteur des expressions rationnelles aurait besoin qu’ils reconnaissent et ignorent le texte variable. Par exemple, dans

(?:abc)*(capture me)(?:bcd)*

vous en aurez besoin si votre entrée peut ressembler à "abcabc me capturer bcdbcd" ou "abc me capturer bcd" ou même simplement "capturer moi".

Autrement dit: si le texte est toujours identique et que vous ne le capturez pas, il n'y a aucune raison d'utiliser des groupes.

8
Yaro

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

1
ydanneg

remplacez les champs de mot de passe de l'entrée:

{"_csrf":["9d90c85f-ac73-4b15-ad08-ebaa3fa4a005"],"originPassword":["uaas"],"newPassword":["uaas"],"confirmPassword":["uaas"]}



  private static final Pattern PATTERN = Pattern.compile(".*?password.*?\":\\[\"(.*?)\"\\](,\"|}$)", Pattern.CASE_INSENSITIVE);

  private static String replacePassword(String input, String replacement) {
    Matcher m = PATTERN.matcher(input);
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
      Matcher m2 = PATTERN.matcher(m.group(0));
      if (m2.find()) {
        StringBuilder stringBuilder = new StringBuilder(m2.group(0));
        String result = stringBuilder.replace(m2.start(1), m2.end(1), replacement).toString();
        m.appendReplacement(sb, result);
      }
    }
    m.appendTail(sb);
    return sb.toString();
  }

  @Test
  public void test1() {
    String input = "{\"_csrf\":[\"9d90c85f-ac73-4b15-ad08-ebaa3fa4a005\"],\"originPassword\":[\"123\"],\"newPassword\":[\"456\"],\"confirmPassword\":[\"456\"]}";
    String expected = "{\"_csrf\":[\"9d90c85f-ac73-4b15-ad08-ebaa3fa4a005\"],\"originPassword\":[\"**\"],\"newPassword\":[\"**\"],\"confirmPassword\":[\"**\"]}";
    Assert.assertEquals(expected, replacePassword(input, "**"));
  }
0
whimmy

Voici une solution différente, qui permet également de remplacer 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 puisse ê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();       
}
0
Jonas_Hess