source

Java regex에서 그룹을 바꿀 수 있습니까?

manysource 2022. 11. 14. 21:46

Java regex에서 그룹을 바꿀 수 있습니까?

이 코드가 있는데 Java regex에서 그룹만 바꿀 수 있는지 알고 싶습니다.코드:

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

    }

사용하다$n(여기서 n은 디짓트)에서 캡처된 서브시퀀스를 참조합니다.첫 번째 그룹은 리터럴 문자열 "number"로, 두 번째 그룹은 첫 번째 그룹의 값으로 대체해야 합니다.

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
}

고려하다(\D+)대신 두 번째 그룹을 위해(.*).*탐욕스러운 매처입니다.처음에는 마지막 숫자를 소비합니다.그런 다음, 투수는 결승을 통과하면 역추적해야 한다.(\d)마지막 숫자와 일치하기 전에 일치시킬 것이 없습니다.

및 를 사용하여 범용 치환 방법을 구축할 수 있습니다.

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

여기에서 온라인 데모를 확인하십시오.

죽은 말을 이기게 되어 미안하지만, 아무도 이것을 지적하지 않는 것은 좀 이상하다. "네, 할 수 있어요, 하지만 이것은 실제로 포획 그룹을 사용하는 방법과는 반대입니다."

Regex를 사용하는 방법으로 사용하는 경우 솔루션은 다음과 같이 단순합니다.

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

아니면 아래 shmosel이 지적한 것처럼

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

...당신의 정규식에서는 소수점을 그룹화할 좋은 이유가 전혀 없기 때문입니다.

일반적으로 삭제할 문자열 부분에 캡처 그룹을 사용하는 것이 아니라 유지할 문자열 부분에 캡처 그룹을 사용합니다.

정말로 치환하고 싶은 그룹은 템플릿엔진(mustache, ejs, StringTemplate 등)을 사용하는 것이 좋습니다.


호기심 많은 사람들을 위한 한편 정규식에서는 캡처되지 않은 그룹도 정규식 엔진에서 변수 텍스트를 인식하고 건너뛸 필요가 있는 경우에 적합합니다.예를 들어,

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

입력이 "bccapture mebcdbcd" 또는 "abcapture mebcd" 또는 "bcapture mebcd"처럼 보일 수도 있고 "discapture me"처럼 보일 수도 있는 경우 필요합니다.

반대로 말하면 텍스트가 항상 같고 캡처하지 않으면 그룹을 사용할 필요가 전혀 없습니다.

matcher.start() 및 matcher.end() 메서드를 사용하여 그룹 위치를 가져올 수 있습니다.이 위치를 사용하면 텍스트를 쉽게 바꿀 수 있습니다.

입력에서 비밀번호 필드를 바꿉니다.

{"_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, "**"));
  }

여기에서는, 1개의 그룹을 복수의 일치로 치환할 수 있는 다른 솔루션을 소개합니다.스택을 사용하여 실행 순서를 되돌리기 때문에 문자열 작업을 안전하게 실행할 수 있습니다.

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

언급URL : https://stackoverflow.com/questions/988655/can-i-replace-groups-in-java-regex