1. Java 정규표현식의 기초
- 정규표현식(Regular Expression)은 문자열을 처리하기 위한 강력한 도구입니다.
- 정규 표현식은 특정한 규칙을 가진 문자열의 집합을 표현하기 위해 사용되는 형식 언어입니다.
- 주로 문자열 검색, 유효성 검사, 데이터 추출 등에 활용됩니다.
- Java에서는
java.util.regex패키지를 통해 정규표현식 기능을 제공하며, 주로Pattern과Matcher클래스를 사용합니다.
1.1 Pattern과 Matcher의 관계
정보
Pattern 객체는 불변(immutable)이며 스레드 안전합니다. 반면 Matcher 객체는 스레드 안전하지 않습니다.
// 기본적인 사용 패턴
Pattern pattern = Pattern.compile("a*b");
Matcher matcher = pattern.matcher("aaaaab");
boolean matches = matcher.matches();
// 일회성 매칭을 위한 간편 메소드
boolean isMatch = Pattern.matches("a*b", "aaaaab");
- Pattern 클래스는 정규 표현식 패턴을 컴파일하고 생성하는 역할을 합니다.
- Matcher 클래스는 패턴을 입력 문자열에 적용하여 매칭 작업을 수행합니다.
2. 정규표현식 문법 상세
2.1 문자 표현
백슬래시 처리
Java 문자열에서 백슬래시를 표현하려면 두 번 이스케이프해야 합니다.
예: 실제 정규식 \d를 Java에서는 \\d로 작성
// 일반 문자
x // 문자 x 자체
\\ // 백슬래시
\t // 탭
\n // 줄바꿈
\r // 캐리지 리턴
\f // 폼피드
2.2 자주 사용되는 문자 클래스
[abc] // a, b, c 중 하나
[^abc] // a, b, c를 제외한 모든 문자
[a-zA-Z] // 영문자
[a-z&&[^m-p]] // a-z에서 m-p를 제외
2.3 미리 정의된 문자 클래스
. // 모든 문자 (줄바꿈 문자 제외)
\d // 숫자: [0-9]
\D // 숫자가 아닌 문자: [^0-9]
\s // 공백 문자: [ \t\n\x0B\f\r]
\S // 공백이 아닌 문자
\w // 단어 문자: [a-zA-Z_0-9]
\W // 단어가 아닌 문자
3. 고급 패턴 매칭
3.1 경계 매칭
^ // 라인의 시작
$ // 라인의 끝
\b // 단어 경계
\B // 비단어 경계
\A // 입력의 시작
\Z // 입력의 끝 (최종 종결자 제외)
\z // 입력의 끝
3.2 수량자(Quantifiers)
탐욕적(Greedy) vs 게으른(Lazy) 수량자
기본적으로 수량자는 탐욕적입니다. 가능한 많은 문자를 매칭하려 합니다. 게으른 수량자(?)를 사용하면 최소한의 문자만 매칭합니다.
// 탐욕적 수량자
X? // X가 0번 또는 1번
X* // X가 0번 이상
X+ // X가 1번 이상
X{n} // X가 정확히 n번
X{n,} // X가 n번 이상
X{n,m} // X가 n번 이상 m번 이하
// 게으른 수량자
X?? // X가 0번 또는 1번 (최소 매칭)
X*? // X가 0번 이상 (최소 매칭)
X+? // X가 1번 이상 (최소 매칭)
3.3 그룹과 캡처
팁
그룹은 왼쪽에서 오른쪽으로 번호가 매겨지며, 전체 패턴은 항상 그룹 0입니다.
// 기본 그룹
(X) // 캡처 그룹
(?:X) // 비캡처 그룹
(?<name>X) // 이름이 있는 캡처 그룹
// 그룹 참조
\1 // 첫 번째 그룹 참조
\k<name> // 이름이 있는 그룹 참조
4. 유니코드 지원
4.1 유니코드 속성
\p{IsHangul} // 한글 문자
\p{IsLatin} // 라틴 문자
\p{Lu} // 대문자
\p{Ll} // 소문자
\p{IsAlphabetic} // 알파벳 문자
4.2 유니코드 스크립트와 블록
// 스크립트
\p{script=Hangul} // 한글 스크립트
\p{sc=Han} // 한자 스크립트
// 블록
\p{block=Hangul} // 한글 블록
\p{blk=CJK} // CJK 블록
5. 실전 패턴 예제
5.1 이메일 주소 검증
public static final Pattern EMAIL_PATTERN = Pattern.compile(
"^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*" +
"@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"
);
public static boolean isValidEmail(String email) {
return EMAIL_PATTERN.matcher(email).matches();
}
5.2 한글 이름 검증
public static final Pattern KOREAN_NAME_PATTERN = Pattern.compile(
"^[가-힣]{2,5}$"
);
public static boolean isValidKoreanName(String name) {
return KOREAN_NAME_PATTERN.matcher(name).matches();
}
6. 성능 최적화
6.1 Pattern 객체 재사용
위험
Pattern.compile()은 비용이 큰 연산입니다. 가능한 static final로 선언하여 재사용하세요.
// 좋은 예
private static final Pattern URL_PATTERN =
Pattern.compile("^https?://[\\w.-]+\\.[a-zA-Z]{2,6}(/.*)?$");
// 피해야 할 예
public boolean isValidUrl(String url) {
return Pattern.matches("^https?://[\\w.-]+\\.[a-zA-Z]{2,6}(/.*)?$", url);
}
6.2 복잡한 패턴 최적화
// 비효율적인 패턴
String inefficientPattern = ".*foo.*"; // 전체 문자열 스캔
// 효율적인 패턴
String efficientPattern = ".*?foo.*?"; // 최소 매칭
// 또는
String moreEfficientPattern = "foo"; // contains() 사용이 더 효율적