Tạo mảng phù hợp với regex


160

Trong Java, tôi đang cố gắng trả lại tất cả các kết quả regex cho một mảng nhưng có vẻ như bạn chỉ có thể kiểm tra xem mẫu đó có khớp với thứ gì đó hay không (boolean).

Làm cách nào tôi có thể sử dụng kết hợp biểu thức chính quy để tạo thành một mảng gồm tất cả các chuỗi khớp với biểu thức regex trong một chuỗi đã cho?


2
Câu hỏi hay. Thông tin bạn tìm kiếm phải là một phần của tài liệu Java trên Regex và Matcher. Đáng buồn thay, nó không phải là.
Cheeso 7/10/2015

3
Một sự xấu hổ thực sự. Chức năng này dường như tồn tại ngoài hộp trong gần như mọi ngôn ngữ khác (có hỗ trợ biểu thức chính quy).
Ray Toal

Câu trả lời:


278

( Câu trả lời của 4 Castle tốt hơn câu trả lời dưới đây nếu bạn có thể giả sử Java> = 9)

Bạn cần tạo một công cụ đối sánh và sử dụng công cụ đó để lặp lại tìm kết quả khớp.

 import java.util.regex.Matcher;
 import java.util.regex.Pattern;

 ...

 List<String> allMatches = new ArrayList<String>();
 Matcher m = Pattern.compile("your regular expression here")
     .matcher(yourStringHere);
 while (m.find()) {
   allMatches.add(m.group());
 }

Sau này, allMatcheschứa các kết quả khớp và bạn có thể sử dụng allMatches.toArray(new String[0])để lấy một mảng nếu bạn thực sự cần.


Bạn cũng có thể sử dụng MatchResultđể viết các hàm trợ giúp để lặp lại các kết quả trùng khớp vì Matcher.toMatchResult()trả về một ảnh chụp nhanh về trạng thái nhóm hiện tại.

Ví dụ, bạn có thể viết một trình vòng lặp lười biếng để cho phép bạn làm

for (MatchResult match : allMatches(pattern, input)) {
  // Use match, and maybe break without doing the work to find all possible matches.
}

bằng cách làm một cái gì đó như thế này:

public static Iterable<MatchResult> allMatches(
      final Pattern p, final CharSequence input) {
  return new Iterable<MatchResult>() {
    public Iterator<MatchResult> iterator() {
      return new Iterator<MatchResult>() {
        // Use a matcher internally.
        final Matcher matcher = p.matcher(input);
        // Keep a match around that supports any interleaving of hasNext/next calls.
        MatchResult pending;

        public boolean hasNext() {
          // Lazily fill pending, and avoid calling find() multiple times if the
          // clients call hasNext() repeatedly before sampling via next().
          if (pending == null && matcher.find()) {
            pending = matcher.toMatchResult();
          }
          return pending != null;
        }

        public MatchResult next() {
          // Fill pending if necessary (as when clients call next() without
          // checking hasNext()), throw if not possible.
          if (!hasNext()) { throw new NoSuchElementException(); }
          // Consume pending so next call to hasNext() does a find().
          MatchResult next = pending;
          pending = null;
          return next;
        }

        /** Required to satisfy the interface, but unsupported. */
        public void remove() { throw new UnsupportedOperationException(); }
      };
    }
  };
}

Với cái này,

for (MatchResult match : allMatches(Pattern.compile("[abc]"), "abracadabra")) {
  System.out.println(match.group() + " at " + match.start());
}

sản lượng

a at 0
b at 1
a at 3
c at 4
a at 5
a at 7
b at 8
a at 10

4
Tôi không đề xuất sử dụng ArrayList ở đây vì bạn không biết trả trước kích thước và có thể muốn tránh thay đổi kích thước bộ đệm. Thay vào đó, tôi thích một LinkedList hơn - mặc dù đó chỉ là một gợi ý và không làm cho câu trả lời của bạn trở nên ít hợp lệ hơn.
Liv

13
@Liv, hãy dành thời gian để điểm chuẩn cả hai ArrayListLinkedList, kết quả có thể gây ngạc nhiên.
Anthony Accioly

Tôi nghe thấy những gì bạn đang nói và tôi nhận thức được tốc độ thực thi và dấu chân bộ nhớ trong cả hai trường hợp, vấn đề với ArrayList là hàm tạo mặc định tạo ra dung lượng 10 - nếu bạn vượt qua kích thước đó bằng các lệnh gọi để thêm ( ) bạn sẽ phải chịu sự phân bổ bộ nhớ và sao chép mảng - và điều đó có thể xảy ra một vài lần. Cấp, nếu bạn mong đợi chỉ một vài trận đấu thì cách tiếp cận của bạn là cách hiệu quả hơn; tuy nhiên nếu bạn thấy rằng mảng "thay đổi kích thước" xảy ra nhiều hơn một lần tôi sẽ đề xuất LinkedList, thậm chí còn nhiều hơn nếu bạn đang xử lý một ứng dụng có độ trễ thấp.
Liv

12
@Liv, Nếu mẫu của bạn có xu hướng tạo ra các kết quả khớp với kích thước khá dễ đoán và tùy thuộc vào việc mẫu đó khớp với thưa thớt hay dày đặc (dựa trên tổng độ dài allMatchesso với yourStringHere.length()), bạn có thể tính toán trước một kích thước tốt allMatches. Theo kinh nghiệm của tôi, chi phí cho LinkedListbộ nhớ và lặp lại hiệu quả-khôn ngoan thường không đáng giá vì vậy LinkedListkhông phải là tư thế mặc định của tôi. Nhưng khi tối ưu hóa một điểm nóng, chắc chắn việc trao đổi danh sách triển khai để xem bạn có được cải thiện hay không.
Mike Samuel

1
Trong Java 9, bây giờ bạn có thể sử dụng Matcher#resultsđể lấy cái Streammà bạn có thể sử dụng để tạo một mảng (xem câu trả lời của tôi ).
4 lâu đài

56

Trong Java 9, bây giờ bạn có thể sử dụng Matcher#results()để lấy cái Stream<MatchResult>mà bạn có thể sử dụng để lấy danh sách / mảng khớp.

import java.util.regex.Pattern;
import java.util.regex.MatchResult;
String[] matches = Pattern.compile("your regex here")
                          .matcher("string to search from here")
                          .results()
                          .map(MatchResult::group)
                          .toArray(String[]::new);
                    // or .collect(Collectors.toList())

1
phương pháp của họ không có kết quả (), vui lòng chạy phương thức này trước
Bravo

14
@Bravo Bạn đang sử dụng Java 9? Nó tồn tại. Tôi liên kết với các tài liệu.
4 lâu đài

: ((có bất kỳ thay thế nào cho java 8
logbasex

25

Java làm cho regex quá phức tạp và nó không theo kiểu perl. Hãy xem MentaRegex để xem làm thế nào bạn có thể thực hiện điều đó trong một dòng mã Java duy nhất:

String[] matches = match("aa11bb22", "/(\\d+)/g" ); // => ["11", "22"]

6
Thật tuyệt. Dấu gạch chéo đôi trông vẫn xấu xí nhưng tôi đoán không có scape từ đó.
JohnPristine

mentaregex-0.9.5.jar, 6Kb đã cứu ngày của tôi, Obrigado Sérgio!
CONvid19

2
CHÚ Ý! Giải pháp tốt nhất. Sử dụng nó!
Vlad Holubiev

13
Là trang web MentaRegex xuống? Khi tôi truy cập mentaregex.soliveirajr.com, nó chỉ nói "hi"
user64141

1
@ user64141 trông giống như vậy
Amit Gold

11

Đây là một ví dụ đơn giản:

Pattern pattern = Pattern.compile(regexPattern);
List<String> list = new ArrayList<String>();
Matcher m = pattern.matcher(input);
while (m.find()) {
    list.add(m.group());
}

(nếu bạn có nhiều nhóm bắt hơn, bạn có thể gọi chúng theo chỉ mục của chúng như là một đối số của phương thức nhóm. Nếu bạn cần một mảng, sau đó sử dụng list.toArray())


mẫu.matches (đầu vào) không hoạt động. Bạn phải vượt qua mô hình regex của mình (một lần nữa!) -> WTF Java?! mẫu.matches (Chuỗi regex, Nhập chuỗi); Bạn có nghĩa là mẫu.matcher (đầu vào)?
El Mac

@ElMac Pattern.matches()là một phương thức tĩnh, bạn không nên gọi nó theo Patternví dụ. Pattern.matches(regex, input)chỉ đơn giản là một tốc ký cho Pattern.compile(regex).matcher(input).matches().
dimo414

5

Từ các đường chính Java chính thức :

        Pattern pattern = 
        Pattern.compile(console.readLine("%nEnter your regex: "));

        Matcher matcher = 
        pattern.matcher(console.readLine("Enter input string to search: "));

        boolean found = false;
        while (matcher.find()) {
            console.format("I found the text \"%s\" starting at " +
               "index %d and ending at index %d.%n",
                matcher.group(), matcher.start(), matcher.end());
            found = true;
        }

Sử dụng findvà chèn kết quả grouptại mảng / Danh sách / bất cứ điều gì.


0
        Set<String> keyList = new HashSet();
        Pattern regex = Pattern.compile("#\\{(.*?)\\}");
        Matcher matcher = regex.matcher("Content goes here");
        while(matcher.find()) {
            keyList.add(matcher.group(1)); 
        }
        return keyList;
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.