Post

Java 정규식에서 subgroup 값 가져오기

Facebook 스프링사용자 모임에 질문이 올라왔다. 아래 코드가 의도와 다른 결과를 뱉는다는 질문이다.

웹에서 정규식을 테스트할수 있는 사이트인 regex101 에서는 정상 작동한다는 말까지 첨부하셧다

1
2
3
4
5
6
7
8
private static final Pattern SIDO_PATTERN = Pattern.compile(
          "(?<sido>\n" +
          "    ([가-힣]+도)|\n" +
          "    ([가-힣]+(특별시|광역시|자치시|자치도))|\n" +
          "    (충북|충남|경북|경남|전북|전남|강원|경기|제주|서울|부산|대구|대전|광주|인천|울산|세종|제주)\n" +
          ")\\s");
SIDO_PATTERN.matcher("서울특별시 강남구 대치동 890-12").matches(); // false
SIDO_PATTERN.matcher("서울특별시 강남구 대치동 890-12").group("sido"); // Match not found

Image

보통 이런경우면 언어마다 미묘하게 다른 정규식 문법의 문제인데… 이번 케이스는 그런것과 좀 다른 특이한 케이스라 혹시 궁금하실분이 있을가봐 정리해둔다

  1. 문제 1: 정규식 패턴의 줄바꿈(\n)과 공백
    • 정규식에 \n이나 공백이 들어가 있으면 기본적으로 해당 공백 문자도 패턴의 일부로 매칭 대상이 됩니다. regex101에서는 공백을 무시하는 x 플래그(VERBOSE 모드)가 기본 켜져 있는 경우가 많지만, 자바에서는 Pattern.COMMENTS 플래그나 (?x)를 주지 않으면 리터럴 문자로 처리되므로 줄바꿈과 공백을 제거해야 합니다.
  2. 문제 2: matches() vs lookingAt() / find()의 매칭 범위 차이
    • matches()는 입력 문자열 전체가 패턴과 완전히 일치해야 true를 반환합니다. 입력값은 "서울특별시 강남구 대치동 890-12"인데 패턴은 앞부분인 "서울특별시 "까지만 정의되어 있으므로 matches()는 당연히 false를 반환합니다.
    • 문자열의 앞부분(접두사)만 매칭하려면 lookingAt(), 문자열 내 임의의 위치에서 매칭을 찾으려면 find()를 사용해야 합니다.
  3. 문제 3: Matcher 인스턴스 재사용 및 매칭 성공 상태
    • 원본 코드에서는 SIDO_PATTERN.matcher(...).matches() 호출 후, group()을 호출할 때 또 다른 matcher(...) 인스턴스를 새로 생성했습니다.
    • 자바의 Matcher는 매칭 함수(matches(), lookingAt(), find())가 성공적으로 true를 반환한 상태의 동일한 인스턴스에서만 group()을 호출할 수 있습니다. 매칭 함수를 호출하지 않았거나 매칭이 실패한 상태에서 group()을 호출하면 IllegalStateException: No match found가 발생합니다.
    • 즉, 자바에서 subgroup 파싱을 위해 lookingAt()이 별도로 필요한 것이 아니라, 문자열 앞부분만 매칭해야 하므로 lookingAt()(또는 find())을 사용해야 하고, 매칭에 성공한 동일한 Matcher 인스턴스에서 group()을 호출해야 합니다.

수정한 코드는 다음과 같다

1
2
3
4
5
6
7
8
9
10
public class TestClass {
    public static void main(String[] args) {
        var matcher = SIDO_PATTERN.matcher("서울특별시 강남구 대치동 890-12");
        matcher.lookingAt();
        System.out.println(matcher.group("sido"));
    }

    private static final Pattern SIDO_PATTERN = Pattern.compile(
            "(?<sido>([가-힣]+도)|([가-힣]+(특별시|광역시|자치시|자치도))|(충북|충남|경북|경남|전북|전남|강원|경기|제주|서울|부산|대구|대전|광주|인천|울산|세종|제주)\\s)");
}

결과

Image

This post is licensed under CC BY 4.0 by the author.