Domanda

Ho questo codice, e voglio sapere, se posso sostituire solo i gruppi (non tutti pattern) in Java espressione regolare. Codice:

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

    }
È stato utile?

Soluzione

Utilizzare $n (dove n è una cifra) per riferirsi a sottosequenze catturati in replaceFirst(...) . Sto assumendo che si voleva sostituire il primo gruppo con la stringa letterale "numero" e il secondo gruppo con il valore del primo gruppo.

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
}

Si consideri (\D+) per il secondo gruppo, invece di (.*). * è un matcher avido, e in un primo momento consumerà l'ultima cifra. Il matcher dovrà poi fare marcia indietro quando si realizza il (\d) finale non ha nulla a partita, prima che possa corrispondere alla cifra finale.

Altri suggerimenti

Si potrebbe utilizzare Matcher#start(group) e Matcher#end(group) per costruire un metodo di sostituzione generico:

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

Verifica demo online qui .

Siamo spiacenti per battere un cavallo morto, ma è sorta-di strano che nessuno fatto notare -. "Sì è possibile, ma questo è l'opposto di come si utilizza gruppi di cattura nella vita reale"

Se si utilizza Regex il modo in cui è destinato a essere utilizzato, la soluzione è semplice come questo:

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

O, come giustamente sottolineato da shmosel seguito,

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

... dal momento che nella tua regex non v'è alcuna buona ragione per raggruppare i decimali a tutti.

Di solito non usa cattura gruppi sulle parti della stringa che si desidera scarto , li usano per parte della stringa che si desidera mantenere .

Se si vuole veramente i gruppi che si desidera sostituire, quello che probabilmente si desidera invece è un motore di template (ad esempio baffi, ejs, StringTemplate, ...).


Per inciso per curiosi, gruppi anche non cattura in regexes sono lì solo per il caso che il motore regex bisogno loro di riconoscere e saltare testo variabile. Per esempio, in

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

avete bisogno se l'input può apparire sia come "abcabc me bcdbcd catturare" o "abc me BCD catturare" o anche solo "me catturare".

o per dirla il contrario:. Se il testo è sempre lo stesso, e non si catturarlo, non v'è alcun motivo per utilizzare i gruppi a tutti

Aggiungere un terzo gruppo con l'aggiunta di parentesi intorno .*, quindi sostituire il sottosequenza con "number" + m.group(2) + "1". per esempio:.

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

È possibile utilizzare matcher.start () e matcher.end metodi () per ottenere le posizioni del gruppo. Quindi, utilizzando questo posiziona potete sostituire facilmente qualsiasi testo.

Ecco una soluzione diversa, che permette anche la sostituzione di un singolo gruppo in più partite. Esso utilizza pile di invertire l'ordine di esecuzione, così l'operazione di stringa può essere eseguita con sicurezza.

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

sostituire i campi password dalla ingresso:

{"_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, "**"));
  }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top