2012-07-03 36 views
5

Có phương pháp API nào trả về tất cả các phần tử (có thể chồng chéo) phù hợp với cụm từ thông dụng không? Ví dụ: Tôi có một chuỗi văn bản: String t = 04/31 412-555-1235; và tôi có mẫu: Pattern p = new Pattern("\\d\\d+"); khớp với chuỗi có từ hai ký tự trở lên.Tất cả các chất nền chồng chéo phù hợp với một regex java

Các kết quả phù hợp tôi nhận được là: 04, 31, 412, 555, 1235.

Làm cách nào để nhận các kết quả trùng lặp?

Tôi muốn mã trở lại: 04, 31, 41, 412, 12, 55, 555, 55, 12, 123, 1235, 23, 235, 35.

Về mặt lý thuyết chúng ta có thể - có một thuật toán rõ ràng là O(n^2) liệt kê và kiểm tra tất cả các chất nền dựa vào mẫu.

EDIT

Thay vì liệt kê tất cả các chuỗi con, nó là an toàn hơn để sử dụng phương pháp region(int start, int end) trong Matcher. Việc kiểm tra mẫu dựa trên chuỗi con được tách riêng, có thể thay đổi kết quả của kết quả phù hợp (ví dụ: nếu có một nhóm không bắt giữ hoặc kiểm tra ranh giới từ ở đầu/cuối mẫu).

EDIT 2

Trên thực tế, không rõ liệu region() làm những gì bạn mong đợi cho trận zero-chiều rộng. Đặc điểm kỹ thuật là mơ hồ, và các thí nghiệm mang lại kết quả đáng thất vọng.

Ví dụ:

String line = "xx90xx"; 
String pat = "\\b90\\b"; 
System.out.println(Pattern.compile(pat).matcher(line).find()); // prints false 
for (int i = 0; i < line.length(); ++i) { 
    for (int j = i + 1; j <= line.length(); ++j) { 
    Matcher m = Pattern.compile(pat).matcher(line).region(i, j); 
    if (m.find() && m.group().size == (j - i)) { 
     System.out.println(m.group() + " (" + i + ", " + j + ")"); // prints 90 (2, 4) 
    } 
    } 
} 

Tôi không chắc chắn những gì các giải pháp thanh lịch nhất là. Một cách tiếp cận sẽ là lấy một chuỗi con là line và đệm bằng các ký tự ranh giới thích hợp trước khi kiểm tra xem các kết quả trùng khớp pat có phù hợp hay không.

EDIT 3

Dưới đây là giải pháp đầy đủ mà tôi đã đưa ra. Nó có thể xử lý các mẫu có độ rộng 0, các đường biên, vv trong biểu thức chính quy ban đầu. Nó xem xét tất cả các chuỗi của chuỗi văn bản và kiểm tra xem biểu thức chính quy chỉ khớp với vị trí cụ thể bằng cách đệm mẫu với số ký tự đại diện thích hợp ở đầu và cuối. Nó dường như làm việc cho các trường hợp tôi đã thử - mặc dù tôi đã không làm thử nghiệm rộng rãi. Nó chắc chắn kém hiệu quả hơn nó có thể.

public static void allMatches(String text, String regex) 
    { 
    for (int i = 0; i < text.length(); ++i) { 
     for (int j = i + 1; j <= text.length(); ++j) { 
     String positionSpecificPattern = "((?<=^.{"+i+"})("+regex+")(?=.{"+(text.length() - j)+"}$))"; 
     Matcher m = Pattern.compile(positionSpecificPattern).matcher(text); 

     if (m.find()) 
     { 
      System.out.println("Match found: \"" + (m.group()) + "\" at position [" + i + ", " + j + ")"); 
     } 
     } 
    } 
    } 

EDIT 4

Dưới đây là một cách tốt hơn để làm điều này: https://stackoverflow.com/a/11372670/244526

EDIT 5

Thư viện JRegex hỗ trợ việc tìm kiếm tất cả các chuỗi con chồng chéo phù hợp với một regex java (mặc dù có vẻ như không được cập nhật trong một thời gian).Cụ thể, số documentation on non-breaking search chỉ định:

Sử dụng tìm kiếm không phá vỡ bạn có thể tìm thấy tất cả các mẫu có thể có của mẫu , bao gồm cả những mẫu được giao nhau hoặc lồng nhau. Đây là đạt được bằng cách sử dụng phương thức của Matcher() thay vì tìm()

+0

chỉ cần thực hiện lặp lại sau regex cho tất cả 3 hoặc nhiều ký tự kết quả –

+0

http://regexlib.com/ có thể là một nơi tốt để thực hiện một số thao tác đào. –

+0

@ Ωmega Đang cố gắng hết sức, nhưng mở ra phản hồi không hữu ích. Chúc mừng. –

Trả lời

0

Gần nhất bạn có thể nhận được là như thế này.

"(?=((\\d*)\\d))(?=(\\d)\\d*)" 

Kết quả sẽ là trong việc nắm bắt nhóm 1, 2 và 3.

Theo như trí tưởng tượng của tôi có thể đi, tôi chỉ có thể nghĩ đến chụp trong điều kiện không có độ dài khẳng định như là một cách hữu hiệu để lấy lại cùng một vị trí của một chuỗi. Việc chụp văn bản bên ngoài xác nhận độ dài bằng 0 sẽ tiêu thụ văn bản một lần và cho tất cả (nhìn sau chỉ có thể chụp chiều dài cố định trong Java, vì vậy nó có thể được coi là không thể truy cập được).

Giải pháp này không hoàn hảo: ngoài việc lặp lại (của văn bản ở cùng một vị trí!) Và kết quả chuỗi trống, nó sẽ không nắm bắt tất cả các dữ liệu có thể có.

Một cách để nắm bắt tất cả các chuỗi con có thể là xây dựng các regex sau đây với giá trị của n bắt đầu từ 1:

"(?=(\\d{" + n + "}))" 

Và phù hợp với chuỗi chống lại điều này cho incrementing giá trị của n cho đến khi không có trận đấu.

Phương pháp này tất nhiên, không hiệu quả so với phương pháp khớp tất cả các số bằng "\ d +" và trích xuất tất cả chuỗi con.

0

Chỉ có thể thực hiện như O (n)chỉ khi bạn chỉ định phạm vi số chiều dài cho phép.

Hãy nói rằng từ 2-4 chữ số (số 00-9999): (?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)

Đây là một sự khẳng định zero-length qua lookahead tích cực, chụp lookahead đó thành các nhóm. Các kết quả là một mảng của tất cả các chuỗi 2-4 chữ số có thể được tìm thấy trong đầu vào regex, cùng với các bản sao và các chuỗi rỗng (đối với các ảnh không khớp).

Tôi không phải là nhà phát triển Java, nhưng tôi tin rằng tập lệnh Perl cũng có thể được đọc làm ví dụ.

#!/usr/bin/perl          # perl script 
use List::MoreUtils qw/ uniq /;      # uniq subroutine library 
$_ = '04/31 412-555-1235';       # input 
my @n = uniq (/(?=(\d{2}))(?=(\1\d)?)(?=(\2\d)?)/g); # regex (single slash in Perl) 
print "$_\n" for grep(/\S/, @n);      # print non-empty lines 

Bí quyết đang sử dụng phần chiếu hậu. Nếu bạn muốn nắm bắt chuỗi 2-5 chữ số, bạn sẽ cần phải sử dụng một lookahead tích cực hơn trong regex: (?=(\\d{2}))(?=(\\1\\d)?)(?=(\\2\\d)?)(?=(\\3\\d)?).

Tôi tin rằng đây là cách tiếp cận gần nhất bạn có thể thực hiện. Nếu điều này làm việc cho bạn, hãy viết một bình luận và hy vọng một số nhà phát triển Java sẽ chỉnh sửa câu trả lời của tôi với mã Java cho kịch bản trên.

+0

Regex là giống nhau trong Java (ngoại trừ dấu gạch chéo ngược cần phải được thoát). Đối với 'uniq', nó có thể được mô phỏng bằng' Set' trong Java ('TreeSet' hoặc' HashSet'). – nhahtdh

+0

@nhahtdh - Cảm ơn. Vui lòng thêm cập nhật cho câu trả lời của tôi bằng cách chỉnh sửa bài đăng. –

1

Tôi gặp phải tình huống tương tự và tôi đã thử các câu trả lời ở trên nhưng trong trường hợp của tôi mất quá nhiều thời gian bằng cách đặt chỉ mục bắt đầu và kết thúc của đối sánh nhưng tôi nghĩ rằng tôi đã tìm được giải pháp tốt hơn đăng nó ở đây cho người khác. Vì vậy, dưới đây là mã sniplet của tôi.

if (textToParse != null) { 
Matcher matcher = PLACEHOLDER_PATTERN.matcher(textToParse); 
    while(matcher.hitEnd()!=true){ 
     Boolean result = matcher.find(); 
     int count = matcher.groupCount(); 
     System.out.println("Result " +result+" count "+count); 
     if(result==true && count==1){ 
      mergeFieldName = matcher.group(1); 
      mergeFieldNames.add(mergeFieldName); 
      } 
     } 
    } 

Tôi đã sử dụng phương thức matcher.hitEnd() để kiểm tra xem tôi có đến cuối văn bản hay không.

Hy vọng điều này sẽ hữu ích. Cảm ơn!

Các vấn đề liên quan