DEV BLOG

[정규표현식] 정규표현식의 non-greedy 매칭

|

greedy와 non-greedy 패턴 매칭의 차이는 무엇일까?


1234라는 문자열을 \d+(숫자 한글자 이상)패턴 매칭을 한다고 하자.

최대한 많이 매칭되는 결과는 1234이다. –> greedy matching

가장 최소한으로 매칭되는 결과는 1일 것이다. –> non-greedy matching


이 둘의 차이를 몰랐던 나는 아래의 케이스에서 삽질을 하고 있었다.


삽질한 예시

아래 html에서 a링크의 href를 그룹매칭해서 가져오고 싶다고 해보자

<a href="https://a.com">a</a><br><a href="https://b.com">b</a>


원하는 결과는 아래와 같다

https://a.com
https://b.com


<a href="(.*)">와 같이 정규표현식으로 그룹매칭을 한다고 한다면 결과가 어떻게 나올까?


Greedy matching

패턴을 (.*)으로 하고 아래 TC를 돌려보았다.

    @Test
    public void test() {
        String html = "<a href=\"https://a.com\">a</a><br><a href=\"https://b.com\">b</a>";
        Pattern linkPattern = Pattern.compile("<a href=\"(.*)\">");

        Matcher matcher = linkPattern.matcher(html);

        while(matcher.find()) {
            System.out.println(matcher.group(1));
        }
    }


<a href="{링크}">의 {링크}값만 출력될 것이라고 생각했지만 결과는 아래와 같았다


https://a.com">a</a><br><a href="https://b.com


정규표현식은 기본적으로 greedy 매칭을 하기 때문이다.

일치하는 것을 최대한 많이 찾으려고 하다보니 위와 같은 결과가 나온 것이다.


Non-greedy matching

결과적으로 내가 원했던 것은 non-greedy 었던 것으로 ?문자를 사용하면 *의 탐욕을 멈출 수 있다고 한다.

수량자(*, +) 바로 뒤에 사용하면, 기본적으로 탐욕스럽던(가능한 한 많이 대응시킴) 수량자를 탐욕스럽지 않게(가능한 가장 적은 문자들에 대응시킴) 한다.

기존의 패턴에 ?을 추가하여 (.*?)으로 TC를 돌려보자

    @Test
    public void test() {
        String html = "<a href=\"https://a.com\">a</a><br><a href=\"https://b.com\">b</a>";
        Pattern linkPattern = Pattern.compile("<a href=\"(.*?)\">");

        Matcher matcher = linkPattern.matcher(html);

        while(matcher.find()) {
            System.out.println(matcher.group(1));
        }
    }


출력값은 원했던 대로 나왔다.

https://a.com
https://b.com


정규표현식 관련 사이트

  • 정규표현식 도식화 사이트 : https://regexper.com/
  • 정규표현식 테스트 사이트 : https://regex101.com/