정규표현식 소개
정규표현식(Regular Expression, 줄여서 Regex)는 문자열 내에서 특정한 패턴을 탐지하거나 조작하는 데 사용되는 강력한 도구입니다. Java에서는 java.util.regex 패키지를 통해 정규표현식 기능을 제공합니다. 이 패키지는 복잡한 텍스트 처리 작업을 간소화할 수 있게 해주며, 입력 검증, 데이터 추출, 문자열 치환 등 다양한 시나리오에 유용하게 쓰입니다.
기본 문법 요소
정규표현식은 특수한 의미를 가진 메타문자들로 구성됩니다. 주요 기호들을 살펴보면 다음과 같습니다.
- .: 줄바꿈 문자를 제외한 모든 단일 문자와 일치.
- \: 다음 문자를 리터럴로 취급 (예:
\.는 마침표 자체를 찾음). - [abc]: a, b, c 중 하나와 일치.
- [^xyz]: x, y, z 외의 모든 문자와 일치.
- [a-z]: 소문자 알파벳 전체 범위.
- \d: 숫자 (
[0-9]와 동일). - *: 앞선 요소가 0회 이상 반복.
- +: 앞선 요소가 1회 이상 반복.
- ?: 앞선 요소가 0회 또는 1회 존재.
- {n,m}: 앞선 요소가 최소 n회, 최대 m회 반복.
- ^: 문자열의 시작 위치.
- $: 문자열의 끝 위치.
- \b: 단어 경계 (공백이나 구두점과 인접한 지점).
- (...): 캡처 그룹 — 매칭된 부분을 저장하여 나중에 참조 가능.
- (?:...): 비캡처 그룹 — 그룹화는 하지만 추출 대상에서 제외.
핵심 클래스: Pattern과 Matcher
Java에서 정규표현식을 사용하려면 두 가지 주요 클래스를 활용해야 합니다.
- Pattern: 정규식을 컴파일하여 재사용 가능한 형태로 만듭니다.
- Matcher: 특정 입력 문자열에 대해 패턴을 적용하고 결과를 분석합니다.
기본 사용 예시
다음 코드는 주어진 문자열이 정규식 패턴에 맞는지 확인합니다.
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexBasics {
public static void main(String[] args) {
String patternString = "a+b*"; // 'a' 한 개 이상, 'b'는 0개 이상
String inputText = "aaab";
Pattern compiledPattern = Pattern.compile(patternString);
Matcher matcher = compiledPattern.matcher(inputText);
if (matcher.matches()) {
System.out.println("전체 일치: 성공");
} else {
System.out.println("전체 일치: 실패");
}
}
}
Matcher의 주요 메서드
| 메서드 | 설명 |
|---|---|
matches() |
입력 전체가 패턴과 정확히 일치하는지 판단. |
find() |
입력 내에서 일치하는 하위 문자열을 순차적으로 탐색. |
group(int) |
지정된 그룹 번호에 해당하는 매칭 결과 반환. |
replaceAll(String) |
모든 일치 항목을 새로운 문자열로 치환. |
실용적인 예제들
이메일 형식 검증
아래 예제는 일반적인 이메일 형식을 검사합니다.
public class EmailCheck {
private static final String EMAIL_PATTERN =
"^[\\w._%+-]+@[\\w.-]+\\.[a-zA-Z]{2,}$";
public static boolean isValidEmail(String email) {
return Pattern.compile(EMAIL_PATTERN)
.matcher(email)
.matches();
}
public static void main(String[] args) {
String[] candidates = {"user@test.com", "bad-email", "admin@site.co.kr"};
for (String e : candidates) {
System.out.println(e + " → " + (isValidEmail(e) ? "유효" : "무효"));
}
}
}
문자열에서 숫자 추출
텍스트 속에 포함된 모든 숫자를 찾아내는 로직입니다.
public class DigitExtractor {
public static void main(String[] args) {
String content = "온도는 25도이며 습도는 60%입니다.";
Pattern digitPattern = Pattern.compile("\\d+");
Matcher matcher = digitPattern.matcher(content);
while (matcher.find()) {
System.out.println("추출된 숫자: " + matcher.group());
}
}
}
특정 패턴 치환
모든 전화번호 형식을 익명화하는 예제입니다.
public class AnonymizePhone {
public static void main(String[] args) {
String text = "연락처: 010-1234-5678, 보조번호: 02-9876-5432";
String masked = text.replaceAll("\\d{2,3}-\\d{3,4}-\\d{4}", "XXX-XXXX-XXXX");
System.out.println(masked);
}
}
고급 기능: 그룹과 어설션
명명된 캡처 그룹
그룹에 이름을 붙여 코드의 가독성을 높일 수 있습니다.
public class NamedGroups {
public static void main(String[] args) {
String logEntry = "ERROR 2025-04-05 User login failed";
String regex = "(?<level>\\w+) (?<date>\\d{4}-\\d{2}-\\d{2}) (?<message>.*)";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(logEntry);
if (m.matches()) {
System.out.println("레벨: " + m.group("level"));
System.out.println("날짜: " + m.group("date"));
System.out.println("메시지: " + m.group("message"));
}
}
}
Lookahead 어설션
다음 문자를 확인하지만 포함하지 않고 매칭합니다.
// 공백 앞에 오는 숫자 찾기
Pattern lookAhead = Pattern.compile("\\d+(?=\\s)");
Matcher match = lookAhead.matcher("남은 사과: 5개");
while (match.find()) {
System.out.println("조건 충족 숫자: " + match.group()); // 출력: 5
}
성능 고려사항
- 자주 사용되는 정규식은 미리 컴파일하여
static final필드로 저장하세요. - 불필요하게 넓은 범위의 양자화자(
.*)는 피하고, 가능하면{n,m}처럼 제한하세요. - 중첩된 그룹이나 과도한 백트래킹을 유발하는 패턴은 성능 저하의 원인이 됩니다.
주요 응용 분야
정규표현식은 아래와 같은 상황에서 특히 유용합니다.
- 입력 폼 검증 (이메일, 전화번호, 우편번호 등)
- 로그 파일 파싱
- 스크래핑을 통한 정보 추출
- 코드 자동 포맷팅 도구
- 데이터 정제 및 ETL 프로세스