Tôi có thể thay thế các nhóm trong Java regex không?


95

Tôi có mã này và tôi muốn biết, nếu tôi có thể chỉ thay thế các nhóm (không phải tất cả các mẫu) trong Java regex. Mã:

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

    }

6
Bạn có thể làm rõ câu hỏi của mình, như có thể đưa ra đầu ra mong đợi cho đầu vào đó không?
Michael Myers

Câu trả lời:


125

Sử dụng $n(với n là một chữ số) để tham chiếu đến các dãy con được bắt trong replaceFirst(...). Tôi giả sử bạn muốn thay thế nhóm đầu tiên bằng chuỗi chữ "số" và nhóm thứ hai bằng giá trị của nhóm đầu tiên.

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
}

Cân nhắc (\D+)cho nhóm thứ hai thay vì (.*). *là một kẻ so khớp tham lam và lúc đầu sẽ sử dụng chữ số cuối cùng. Người so khớp sau đó sẽ phải quay lại khi nhận ra kết quả cuối cùng (\d)không có gì khớp, trước khi nó có thể khớp với chữ số cuối cùng.


7
Sẽ được tốt đẹp nếu bạn đã có thể đăng một đầu ra ví dụ
winklerrr

6
Công trình này vào trận đấu đầu tiên, nhưng wont work nếu có nhiều nhóm và bạn đang iterating qua chúng với một thời gian (m.find ())
Hugo Zaragoza

1
Tôi đồng ý với Hugo, đây là một cách khủng khiếp để thực hiện giải pháp ... Tại sao trên Trái đất đây lại là câu trả lời được chấp nhận mà không phải câu trả lời của acdcjunior - đó là giải pháp hoàn hảo: số lượng mã nhỏ, tính liên kết cao và khớp nối thấp, cơ hội ít hơn nhiều (nếu không phải là không có cơ hội) các tác dụng phụ không mong muốn ... thở dài ...
FireLight

Câu trả lời này hiện không hợp lệ. Các m.replaceFirst("number $2$1");nênm.replaceFirst("number $3$1");
Daniel Eisenreich

52

Bạn có thể sử dụng Matcher#start(group)Matcher#end(group)xây dựng một phương pháp thay thế chung:

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

Kiểm tra bản demo trực tuyến tại đây .


1
Đây thực sự nên là câu trả lời được chấp nhận, nó là giải pháp hoàn chỉnh nhất và "sẵn sàng hoạt động" mà không cần giới thiệu mức độ khớp nối với mã đi kèm. Mặc dù tôi khuyên bạn nên thay đổi tên phương thức của một trong những tên đó. Thoạt nhìn, nó giống như một cuộc gọi đệ quy trong phương thức đầu tiên.
FireLight

Đã bỏ lỡ cơ hội chỉnh sửa. Lấy lại phần về cuộc gọi đệ quy, không phân tích mã đúng cách. Quá tải hoạt động tốt cùng nhau
FireLight

23

Rất tiếc khi đánh chết một con ngựa, nhưng thật kỳ lạ là không ai chỉ ra điều này - "Có bạn có thể, nhưng điều này ngược lại với cách bạn sử dụng các nhóm bắt trong cuộc sống thực".

Nếu bạn sử dụng Regex theo cách nó được sử dụng, thì giải pháp đơn giản như sau:

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

Hoặc như được chỉ ra một cách đúng đắn bởi shmosel bên dưới,

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

... vì trong regex của bạn, không có lý do chính đáng nào để nhóm các số thập phân cả.

Bạn không thường sử dụng các nhóm thu thập trên các phần của chuỗi mà bạn muốn loại bỏ , bạn sử dụng chúng trên phần của chuỗi mà bạn muốn giữ lại .

Nếu bạn thực sự muốn các nhóm mà bạn muốn thay thế, những gì bạn có thể muốn thay thế là một công cụ tạo khuôn mẫu (ví dụ: ria mép, ejs, StringTemplate, ...).


Để dành cho những nhóm tò mò, ngay cả những nhóm không nắm bắt trong regexes cũng chỉ ở đó cho trường hợp công cụ regex cần họ nhận ra và bỏ qua văn bản biến đổi. Ví dụ, trong

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

bạn cần chúng nếu thông tin đầu vào của bạn có thể giống như "abcabc bắt tôi bcdbcd" hoặc "abc bắt tôi bcd" hoặc thậm chí chỉ "chụp tôi".

Hay nói theo cách khác: nếu văn bản luôn giống nhau và bạn không nắm bắt được nó, thì không có lý do gì để sử dụng nhóm cả.


1
Các nhóm không bắt là không cần thiết; \d(.*)\dsẽ đủ.
shmosel

1
Tôi không hiểu $11ở đây. Tại sao 11?
Alexis

1
@Alexis - Đây là một câu hỏi hóc búa của java regex: nếu nhóm 11 chưa được đặt, java sẽ hiểu $ 11 là $ 1 theo sau là 1.
Yaro

9

Thêm nhóm thứ ba bằng cách thêm các parens xung quanh .*, sau đó thay thế dãy con bằng "number" + m.group(2) + "1". ví dụ:

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

4
Trên thực tế, Matcher hỗ trợ kiểu tham chiếu $ 2, vì vậy m.replaceFirst ("số $ 21") sẽ làm điều tương tự.
Michael Myers

Trên thực tế, họ không làm điều tương tự. "number$21"hoạt động và "number" + m.group(2) + "1"không.
Alan Moore

2
Có vẻ như number$21sẽ thay thế nhóm 21, không phải nhóm 2 + chuỗi "1".
Fernando M. Pinheiro

Đây là nối chuỗi đơn giản, phải không? tại sao chúng ta cần gọi là ReplaceFirst?
Zxcv Mnb

2

Bạn có thể sử dụng các phương thức matcher.start () và matcher.end () để nhận vị trí nhóm. Vì vậy, sử dụng các vị trí này, bạn có thể dễ dàng thay thế bất kỳ văn bản nào.


1

thay thế các trường mật khẩu từ đầu vào:

{"_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

Đây là một giải pháp khác, cũng cho phép thay thế một nhóm duy nhất trong nhiều trận đấu. Nó sử dụng ngăn xếp để đảo ngược thứ tự thực hiện, do đó, hoạt động chuỗi có thể được thực hiện một cách an toàn.

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();       
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.