2012-03-01 23 views
32

Tôi có một tập hợp các từ nói - táo, cam, lê, chuối, kiwiSử dụng Java Regex, cách kiểm tra xem chuỗi có chứa bất kỳ từ nào trong tập hợp không?

Tôi muốn kiểm tra xem câu có chứa bất kỳ từ nào được liệt kê ở trên không, và nếu có, tôi muốn tìm từ nào phù hợp. Làm thế nào tôi có thể thực hiện điều này trong Regex?

Tôi hiện đang gọi String.indexOf() cho từng bộ từ của tôi. Tôi giả định điều này là không hiệu quả như một kết hợp regex?

Trả lời

47

TL; DR Đối với chuỗi con đơn giản là tốt nhất nhưng đối với chỉ phù hợp với toàn bộ các từ Regular Expression có lẽ tốt hơn.

Cách tốt nhất để xem phương pháp nào hiệu quả hơn là kiểm tra phương pháp đó.

Bạn có thể sử dụng String.contains() thay vì String.indexOf() để đơn giản hóa mã không phải regexp của bạn.

Để tìm kiếm các từ khác nhau Regular Expression trông như thế này:

apple|orange|pear|banana|kiwi 

Các | công trình như một OR trong Regular Expressions.

mã kiểm tra rất đơn giản của tôi trông như thế này:

public class TestContains { 

    private static String containsWord(Set<String> words,String sentence) { 
    for (String word : words) { 
     if (sentence.contains(word)) { 
     return word; 
     } 
    } 

    return null; 
    } 

    private static String matchesPattern(Pattern p,String sentence) { 
    Matcher m = p.matcher(sentence); 

    if (m.find()) { 
     return m.group(); 
    } 

    return null; 
    } 

    public static void main(String[] args) { 
    Set<String> words = new HashSet<String>(); 
    words.add("apple"); 
    words.add("orange"); 
    words.add("pear"); 
    words.add("banana"); 
    words.add("kiwi"); 

    Pattern p = Pattern.compile("apple|orange|pear|banana|kiwi"); 

    String noMatch = "The quick brown fox jumps over the lazy dog."; 
    String startMatch = "An apple is nice"; 
    String endMatch = "This is a longer sentence with the match for our fruit at the end: kiwi"; 

    long start = System.currentTimeMillis(); 
    int iterations = 10000000; 

    for (int i = 0; i < iterations; i++) { 
     containsWord(words, noMatch); 
     containsWord(words, startMatch); 
     containsWord(words, endMatch); 
    } 

    System.out.println("Contains took " + (System.currentTimeMillis() - start) + "ms"); 
    start = System.currentTimeMillis(); 

    for (int i = 0; i < iterations; i++) { 
     matchesPattern(p,noMatch); 
     matchesPattern(p,startMatch); 
     matchesPattern(p,endMatch); 
    } 

    System.out.println("Regular Expression took " + (System.currentTimeMillis() - start) + "ms"); 
    } 
} 

Các kết quả tôi nhận được là như sau:

Contains took 5962ms 
Regular Expression took 63475ms 

Rõ ràng timings sẽ thay đổi tùy theo số lượng từ được tìm kiếm và Các chuỗi được tìm kiếm, nhưng dường như nhanh hơn 10 lần so với các cụm từ thông dụng cho một tìm kiếm đơn giản như thế này.

Bằng cách sử dụng biểu thức chính quy để tìm kiếm chuỗi bên trong chuỗi khác, bạn đang sử dụng búa tạ để bẻ khóa đai ốc vì vậy tôi đoán chúng ta không nên ngạc nhiên vì nó chậm hơn. Lưu biểu thức chính quy khi các mẫu bạn muốn tìm phức tạp hơn.

Một trường hợp bạn có thể muốn sử dụng Regular Expressions là nếu indexOf() và sẽ không thực hiện công việc vì bạn chỉ muốn để phù hợp với toàn bộ từ và không chỉ chuỗi con, ví dụ bạn muốn khớp với pear nhưng không khớp với spears. Biểu thức chính quy xử lý trường hợp này tốt khi chúng có khái niệm là word boundaries.

Trong trường hợp này, chúng tôi muốn thay đổi mô hình của chúng tôi để:

\b(apple|orange|pear|banana|kiwi)\b 

Các \b nói với chỉ phù hợp với đầu hoặc cuối của một từ và nhóm ngoặc OR biểu thức với nhau.

Lưu ý, khi xác định mô hình này trong mã của bạn, bạn cần phải thoát khỏi dấu xồ nguợc với dấu chéo ngược khác:

Pattern p = Pattern.compile("\\b(apple|orange|pear|banana|kiwi)\\b"); 
7

Tôi không nghĩ rằng một regexp sẽ làm một công việc tốt hơn về hiệu suất nhưng bạn có thể sử dụng nó như sau:

Pattern p = Pattern.compile("(apple|orange|pear)"); 
Matcher m = p.matcher(inputString); 
while (m.find()) { 
    String matched = m.group(1); 
    // Do something 
} 
+5

Bạn không thể đọc được? Tôi chưa bao giờ nói nó hiệu quả. –

+1

Hiệu suất phụ thuộc vào độ dài regex. Nếu nó nhỏ hơn 1000 ký tự, hãy tiếp tục. Nếu nó còn bạn cần giải pháp khác. Ví dụ: tách văn bản để tách các từ và kiểm tra chúng dựa vào bảng băm được xác định trước/tập các từ "đã biết". – AlexR

+2

@deporter mục đích của câu trả lời là đưa ra một gợi ý tốt về cách giải quyết câu hỏi không cung cấp một giải pháp hoàn hảo, sáng bóng, đẳng cấp thế giới. Nó có thể dễ dàng được cải thiện và cho khả năng đọc, nếu bạn có 200 chuỗi (một lý do nữa không sử dụng regexp cho điều đó), bạn có thể sử dụng vòng lặp for và nối trong một 'StringBuilder'. Tôi nghĩ rằng câu trả lời của tôi cung cấp đủ hương vị. –

2

Dưới đây là giải pháp đơn giản nhất mà tôi tìm thấy (phù hợp với các kí hiệu):

boolean a = str.matches(".*\\b(wordA|wordB|wordC|wordD|wordE)\\b.*"); 
Các vấn đề liên quan