2012-03-01 2 views
32

나는 사과, 오렌지, 배, 바나나, 키위를 말하고있다.Java Regex를 사용하여 문자열에 세트의 단어가 포함되어 있는지 확인하는 방법은 무엇입니까?

위의 단어 중 하나라도 문장에 포함되어 있는지 확인하고 싶으면 어떤 단어를 찾고 싶다. 일치. Regex에서 이것을 어떻게 수행 할 수 있습니까?

현재 각 단어 집합에 대해 String.indexOf()를 호출하고 있습니다. 나는 이것이 정규 표현식만큼 효율적이지 않다고 가정하고 있는가?

답변

47

TL; DR을 단순 문자열 contains()하시는 것이 좋습니다하지만입니다 정규 표현식 만 일치하면됩니다.

어떤 방법이 더 효율적인 지 확인하는 가장 좋은 방법은 테스트하는 것입니다.

String.indexOf() 대신 String.contains()을 사용하면 정규식이 아닌 코드를 단순화 할 수 있습니다.

는 정규 표현식은 다음과 같습니다 다른 단어를 검색하려면 : 정규 표현식에 OR

apple|orange|pear|banana|kiwi 

| 작동합니다.

내 아주 간단한 테스트 코드는 다음과 같습니다 : 단어의 수에 따라 달라집니다

분명히
Contains took 5962ms 
Regular Expression took 63475ms 

타이밍은 검색과하고 다음과 같이 내가 가진

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

결과가 있었다 검색되는 문자열이지만 은 이와 같은 간단한 검색에서 정규 표현식보다 10 배 빠릅니다.

정규 표현식을 사용하여 다른 문자열 안에있는 문자열을 검색하면 너트를 깨는 데 쓰레기를 사용하고 있으므로 우리는 더 느리다는 사실에 놀라지 말아야합니다. 찾을 패턴이 더 복잡한 경우 정규 표현식을 저장하십시오. 당신은 예를 들어, 전체 단어뿐 아니라 문자열을 일치시킬 때문에 indexOf()contains() 일을하지 않을 경우 정규 표현식을 사용할 수 있습니다

한 경우입니다 pear과 일치하고 싶지 만 spears이 아닙니다. 정규식은 word boundaries이라는 개념을 가지고 있으므로이 사례를 잘 처리합니다.

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

\b 만 또는 식을 함께 시작 또는 단어의 끝과 괄호 그룹 일치에 말한다 :

이 경우에 우리는 우리의 패턴을 바꿀 것입니다.코드에서이 패턴을 정의 할 때

주, 당신은 다른 백 슬래시 백 슬래시를 이스케이프해야합니다 여기

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

나는 정규 표현식은 성능면에서 더 나은 일을 할 것입니다 생각하지 않습니다하지만 당신은 다음과 같이 사용할 수 있습니다 :

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

그냥 읽을 수 없니? 나는 그것이 효율적이라고 결코 말하지 않았다. –

+1

성능은 정규 표현식 길이에 따라 다릅니다. 1000 자 미만인 경우 계속하십시오. 길면 다른 해결책이 필요합니다. 예를 들어 텍스트를 분리하여 단어를 분리하고 사전 정의 된 해시 테이블/"알려진"단어 세트와 대조하십시오. – AlexR

+2

@deporter 답변의 목적은 완벽하고 반짝이는 세계적인 솔루션을 제공하지 말아야하는 문제를 해결하는 방법에 대한 좋은 힌트를 제공하는 것입니다. 쉽게 향상시킬 수 있고 가독성을 위해 200 개의 문자열 (regexp를 사용하지 않는 또 다른 이유)이 있으면 for 루프를 사용하고'StringBuilder'에서 연결할 수 있습니다. 나는 나의 대답이 충분한 풍미를 제공한다고 생각한다. –

2

내가 찾은 가장 간단한 솔루션 (와일드 카드와 일치)입니다 :

boolean a = str.matches(".*\\b(wordA|wordB|wordC|wordD|wordE)\\b.*");