¿Puedo reemplazar grupos en Java regex?

Tengo este código, y quiero saber si puedo reemplazar solo los grupos (no todos los patrones) en Java regex. Código:

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

Use $n (donde n es un dígito) para referirse a las subsecuencias capturadas en replaceFirst(...) . Supongo que quería reemplazar el primer grupo con el “número” de cadena literal y el segundo grupo con el valor del primer grupo.

 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 } 

Considere (\D+) para el segundo grupo en lugar de (.*) . * es un matcher ambicioso, y al principio consumirá el último dígito. El emparejador deberá retroceder cuando se dé cuenta de que el final (\d) no tiene nada que hacer coincidir, antes de que pueda coincidir con el dígito final.

Puede usar Matcher#start(group) y Matcher#end(group) para construir un método de reemplazo genérico:

 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("([az]+)([0-9]+)([az]+)", "aaa123ccc", 1, "%")); // replace with "!!!" what was matched the 4th time by the group 2 // input: a1b2c3d4e5 // output: a1b2c3d!!!e5 System.out.println(replaceGroup("([az])(\\d)", "a1b2c3d4e5", 2, 4, "!!!")); } 

Verifique la demostración en línea aquí .

Agregue un tercer grupo al agregar parens alrededor de .* , Luego reemplace la subsecuencia con "number" + m.group(2) + "1" . p.ej:

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

Perdón por vencer a un caballo muerto, pero es algo raro que nadie lo haya señalado: “Sí, puedes, pero esto es lo contrario de cómo usas grupos de captura en la vida real”.

Si usa Regex de la manera en que debe usarse, la solución es tan simple como esto:

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

O como lo señala legítimamente Shmosel a continuación,

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

… ya que en tu expresión regular no hay una buena razón para agrupar los decimales en absoluto.

Usualmente no usas grupos de captura en las partes de la cadena que deseas descartar , las usas en la parte de la cadena que deseas conservar .

Si realmente desea grupos que desea reemplazar, lo que probablemente desee es un motor de plantillas (por ejemplo, bigote, ejs, StringTemplate, …).


Como un lado para los curiosos, incluso los grupos que no capturan en expresiones regulares están ahí para el caso de que el motor de expresiones regulares necesite que reconozcan y salten el texto variable. Por ejemplo, en

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

los necesita si su entrada puede verse como “abcabc capture me bcdbcd” o “abc capture me bcd” o incluso simplemente “capture me”.

O para decirlo de otro modo: si el texto es siempre el mismo y no lo captura, no hay ninguna razón para usar grupos en absoluto.

Puede utilizar los métodos matcher.start () y matcher.end () para obtener las posiciones del grupo. Entonces, usando estas posiciones puede reemplazar fácilmente cualquier texto.

Aquí hay una solución diferente, que también permite el reemplazo de un solo grupo en múltiples coincidencias. Utiliza stacks para invertir el orden de ejecución, por lo que la operación de cadena se puede ejecutar con seguridad.

 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 replaceStrategy) { Stack startPositions = new Stack<>(); Stack 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(); }