Posso replace grupos em regex Java?

Eu tenho esse código, e eu quero saber, se eu posso replace apenas grupos (nem todos os padrões) em regex Java. 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 (onde n é um dígito) para se referir a subsequências capturadas em replaceFirst(...) . Eu estou supondo que você queria replace o primeiro grupo com a seqüência literal “número” e o segundo grupo com o valor do primeiro 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 o segundo grupo em vez de (.*) . * é um combinador ganancioso e, a princípio, consumirá o último dígito. O matcher terá que retroceder quando perceber que o final (\d) não tem nada a combinar, antes que ele possa corresponder ao dígito final.

Você pode usar o Matcher#start(group) e o Matcher#end(group) para criar um método de substituição 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 a demonstração online aqui .

Adicione um terceiro grupo adicionando parens ao redor .* , Depois substitua a subsequência por "number" + m.group(2) + "1" . por exemplo:

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

Desculpe bater em um cavalo morto, mas é meio estranho que ninguém tenha apontado isso – “Sim, você pode, mas isso é o oposto de como você usa grupos de captura na vida real”.

Se você usar o Regex da maneira como ele deve ser usado, a solução é simples assim:

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

Ou como corretamente apontado por shmosel abaixo,

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

… já que no seu regex não há uma boa razão para agrupar os números decimais.

Você não costuma usar grupos de captura nas partes da string que deseja descartar , você os usa na parte da string que deseja manter .

Se você realmente quer grupos que deseja replace, o que você provavelmente quer é um mecanismo de modelagem (por exemplo, bigode, ejs, StringTemplate, …).


Como um aparte para os curiosos, até mesmo os grupos que não capturam em expressões regulares estão lá apenas para o caso em que o mecanismo regex precisa deles para reconhecer e pular o texto variável. Por exemplo, em

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

você precisa deles se sua input puder parecer tanto “abcabc capture me bcdbcd” ou “abc capture me bcd” ou até mesmo “capture me”.

Ou, em outras palavras: se o texto é sempre o mesmo e você não o captura, não há motivo para usar grupos.

Você pode usar os methods matcher.start () e matcher.end () para obter as posições do grupo. Então, usando essas posições, você pode facilmente replace qualquer texto.

Aqui está uma solução diferente, que também permite a substituição de um único grupo em várias correspondências. Ele usa pilhas para reverter a ordem de execução, portanto, a operação de seqüência de caracteres pode ser executada com segurança.

 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(); }