정규 표현식(Regular Expression)

비딴·2024년 1월 6일
0

글의 목표

자바에서 함수를 사용할 때, 정규 표현식을 이용하여 특정 패턴을 추출, 치환, 삭제하는 경우가 많았습니다. String.matches(), String.replaceAll(), String.split()
정규 표현식에 대해 익히고 함수들을 유연하고 깔끔하게 쓰기를 희망합니다.

정규 표현식이란?

문자열의 일정한 패턴을 표현하는 방식입니다.
예를 들어, "ABC DEF GHI" 는 대문자 알파벳 3개와 공백으로 이루어져 있습니다.
이러한 대문자 알파벳 3개와 공백 있을 수도 있고 없을 수도 있는 정규표현식으로 표현할 수 있습니다.

구조

패턴구분자 시작작성할 패턴패턴 구분자 끝flag
/패턴/gm

정규 표현식은 이러한 구조를 띄인다고 합니다.

이 글에서는 구조 중 패턴에 대해 알아볼 것이기 때문에 인지만 하고 넘어가겠습니다.

패턴

패턴은 메타문자로 표현됩니다.
메타문자는 정규식에서 특별한 의미를 지닌 문자입니다. 메타 문자를 이용하여 여러 형태의 패턴을 표현할 수 있습니다.
(문자가 메타문자인 경우, default 메타문자로 인식합니다. 메타문자를 문자로 쓰고 싶은 경우 \ 를 사용하여 변환할 수 있습니다)
메타문자 뒤에 수량자를 표현할 수도 있습니다. 일반적으로 1개일 때는 생략을 하고 최소, 최대, 범위를 지정할 수 있습니다.

그룹, 범위

문자설명
|또는
()그룹
[]문자셋, 괄호 안의 어떤 문자일 경우
[^]부정 문자셋, 괄호 안의 어떤 문자도 아닌 경우
?:찾지만 기억하지 않음

수량자(Quantifiers)

문자설명
?1개 이하 (zero or one)
*0개 이상 (zero or more)
+1개 이상 (one or more)
{n}n번
{min,}최소
{min,max}최소 그리고 최대

경계(Boundary-type)

문자설명
\b단어 경계
\B단어 경계가 아닌
^문장의 시작
$문장의 끝

Character classes

문자설명
\ 특수 문자가 아닌 문자로 표기
.어떤 글자 (줄바꿈 문자 제외)
\ddigit 숫자
\Ddigit 숫자가 아님
\wword 문자
\Wword 문자 아님
\sspace 공백
\Sspace 공백 아님

이외에도 다양한 메타 문자와 백슬래시 시퀀스가 존재합니다. 메타 문자 설명 IBM

자바 활용

자바 예제

class RegexTest {

    String PHONE_NUMBER_REGEX = "\\d{2,3}[- .]\\d{3}[- .]\\d{4}";
    String EMAIL_REGEX = "[a-zA-Z0-9._+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9.]+";
    String YOUTUBE_ADDRESS_REGEX = "(https?:\\/\\/)?(www\\.)?youtu.be\\/([a-zA-Z0-9-]{11})";

    Pattern PHONE_NUMBER_PATTERN = Pattern.compile(PHONE_NUMBER_REGEX);
    Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
    Pattern YOUTUBE_ADDRESS_PATTERN = Pattern.compile(YOUTUBE_ADDRESS_REGEX);

    @Test
    void normalizeDots() {
        String input = "This....is.....a......test....";
        String output = input.replaceAll("\\.{2,}", ".");
        assertThat(output).isEqualTo("This.is.a.test.");
    }

    @Test
    void matchText() {
        String text = "This is a test string.";

        Pattern pattern = Pattern.compile("^[a-zA-Z]+$");
        Matcher matcher = pattern.matcher(text);

        assertThat(matcher.matches()).isFalse();
    }

    @ParameterizedTest
    @ValueSource(strings = {
            "010-898-0893",
            "010 898 0893",
            "010.898.0893",
            "010-405-3412",
            "02-878-8888"
    })
    void matchPhoneNumber(String phoneNumber) {
        assertThat(PHONE_NUMBER_PATTERN.matcher(phoneNumber).matches()).isTrue();
    }

    @ParameterizedTest
    @ValueSource(strings = {
            "dream.coder.ellie@gmail.com",
            "hello@daum.net",
            "hello@daum.co.kr"
    })
    void matchEMAIL(String email) {

        assertThat(EMAIL_PATTERN.matcher(email).matches()).isTrue();
    }

    @ParameterizedTest
    @ValueSource(strings = {
            "http://www.youtu.be/-ZClicWm0zM",
            "https://www.youtu.be/-ZClicWm0zM",
            "https://youtu.be/-ZClicWm0zM",
            "youtu.be/-ZClicWm0zM"
    })
    void matchYouTubeAddress(String youtubeAddress) {
        assertThat(YOUTUBE_ADDRESS_PATTERN.matcher(youtubeAddress).matches()).isTrue();
    }
}

자바에서 정규표현식 성능 향상하기

String.replaceAll()Pattern.compile()를 이용하여 새로운 패턴을 만듭니다. 반복하여 수행할 경우, 정적 Pattern을 만든 다음 사용하면 성능적 이점을 누릴 수 있습니다.
Pattern.compile()을 통해 Pattern을 인스턴스화하고 Pattern.matcher()를 통해 패턴 검사를 하면 됩니다.

주의할 점

  • 복잡한 패턴의 경우 Catastrophic backtracking이 발생할 수 있습니다. 패턴을 명확하게 표현하여야 합니다. Catastrophic backtracking 발생과 해결방안
  • 가독성을 헤칠 수 있습니다. 때로는 함수를 따로 빼서 작성하는 것이 바람직 할 수 있습니다.

정규표현식 연습하기

프로그래머스 - 정규표현식
regexone

참조

[드림코딩 - 정규 표현식] 유투브 영상
10분 테코톡 - 정규 표현식

profile
비 온 뒤 딴딴

0개의 댓글