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
보통 이런경우면 언어마다 미묘하게 다른 정규식 문법의 문제인데… 이번 케이스는 그런것과 좀 다른 특이한 케이스라 혹시 궁금하실분이 있을가봐 정리해둔다
- 문제 1: 정규식 패턴의 줄바꿈(
\n)과 공백- 정규식에
\n이나 공백이 들어가 있으면 기본적으로 해당 공백 문자도 패턴의 일부로 매칭 대상이 됩니다. regex101에서는 공백을 무시하는x플래그(VERBOSE 모드)가 기본 켜져 있는 경우가 많지만, 자바에서는Pattern.COMMENTS플래그나(?x)를 주지 않으면 리터럴 문자로 처리되므로 줄바꿈과 공백을 제거해야 합니다.
- 정규식에
- 문제 2:
matches()vslookingAt()/find()의 매칭 범위 차이matches()는 입력 문자열 전체가 패턴과 완전히 일치해야true를 반환합니다. 입력값은"서울특별시 강남구 대치동 890-12"인데 패턴은 앞부분인"서울특별시 "까지만 정의되어 있으므로matches()는 당연히false를 반환합니다.- 문자열의 앞부분(접두사)만 매칭하려면
lookingAt(), 문자열 내 임의의 위치에서 매칭을 찾으려면find()를 사용해야 합니다.
- 문제 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)");
}
결과
This post is licensed under CC BY 4.0 by the author.

