본문 바로가기
프로그래밍/자바

자바 정규식(Regular Expression) 사용하기

by pentode 2019. 8. 10.

앞의 글에서 문자열을 분할하고, 내용을 바꾸는 split(),  replaceAll() 메소드에 대해서 알아보았는데, 정규식을 사용하는 예를 들었었습니다. 그래서 자바언어에서의 정규식에 대해서 알아보려고 합니다.

 

정규식은 문자열에서 특정 패턴을 찾거나 교체, 삭제 등 문자열을 원하는 형태로 편집하는 기능을 제공해 줍니다. 정규식은 자주 사용되지는 않지만, 어떤 경우에 일반적인 로직으로 처리하기 힘든것은 간편하게 처리할 수 있는 힘이 있습니다. 대부분의 언어들이 정규식을 가지고 있는데, 그 사용법들이 조금씩 다른것 같습니다.

 

 

자바 문자열 객체(java.lang.String)에서 정규식을 사용하는 자주 쓰는 메소드는 다음과 같습니다.

 

- boolean matches(String regex)

  인자로 주어진 정규식에 매칭되는 값이 있는지 확인합니다.

 

- String replaceAll(String regex, String replacement)

  문자열내에 있는 정규식 regex와 매치되는 모든 문자열을 replacement문자열로 바꾼 문자열을 반환합니다.

 

- String[] split(String regex)

  인자로 주어진 정규식과 매치되는 문자열을 구분자로 분할합니다.

 

정규식을 전문적으로 다루는 객체도 제공됩니다. java.util.regex 패키지에 있는 다음 세가지 클래스들 입니다. 주요한 차이점은 정규식을 Pattern 객체로 미리 컴파일 해둘수 있어서 처리 속도가 좀 더 빠르고, 매칭된 데이터를 좀더 상세히 다룰 수 있습니다.

 

- Pattern 객체

  정규식의 컴파일된 표현 입니다(정규식을 적용 가능하도록 컴파일해서 가지고 있습니다).

  Pattern 클래스는 공개된 생성자를 제공하지 않습니다.

  패턴을 생성하려면 Pattern객체를 반환하는 정적 compile 메소드를 호출해야 합니다.

  이 메소드는 첫 번째 인자로 정규식 문자열을 받아 들입니다.

 

- Matcher 객체

  패턴을 해석하고 입력 문자열에 대해 일치 작업을 수행하는 엔진입니다.

  Pattern 클래스와 마찬가지로 Matcher는 어떤 공개된 생성자도 정의하고 있지 않습니다.

  Matcher객체는 Pattern 객체의 matcher 메소드를 호출해서 얻습니다.

 

- PatternSyntaxException 객체

  정규식 패턴의 문법 오류를 나타내는 unchecked 예외 입니다.

 

Pattern과 Matcher를 사용해서 문자열에서 패턴을 찾는 예제 입니다.

 

package com.tistory.offbyone;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReplaceTest {
    public static void main(String[] args) {

        StringBuilder sb = new StringBuilder();
        Pattern pattern = Pattern.compile("<[^<>]*>");
        Matcher matcher = pattern.matcher("배를 먹습니다. <br />사과를 먹습니다.");

        boolean found = false;

        while(matcher.find()) {
            sb.append("텍스트 \"")
              .append(matcher.group())      // 찾은 문자열 그룹 입니다.
              .append("\"를 찾았습니다.\n")
              .append("인덱스 ")
              .append(matcher.start())      // 찾은 문자열의 시작 위치 입니다.
              .append("에서 시작하고, ")
              .append(matcher.end())        // 찾은 문자열의 끝 위치 입니다.
              .append("에서 끝납니다.\n");
              found = true;
        }

        if(!found) {
            sb.append("찾지 못했습니다.");
        }

        System.out.println(sb.toString());
    }
}

결과)
텍스트 "<br />"를 찾았습니다.
인덱스 9에서 시작하고, 15에서 끝납니다.

 

Pattenr과 Matcher를 사용하면 찾은 문자열을 하나씩 처리할 수 있습니다.

 

이제 정규식에 사용되는 메타문자의 사용법을 알아보겠습니다.

 

 

1. 매칭될 문자를 지정하거나, 제외하는 방법입니다.

 

정규식 설명
[abc] a, b, c중 하나이면 일치 합니다.
[^abc] a, b, c를 제외한 다른 글자 이면 일치합니다.
[a-zA-Z] a 부터 z까지의 소문자 알파벳 이거나 A 부터 Z까지의 대문자 알파벳 중의 하나라면 일치합니다.(범위)
[a-d[m-p]] a 부터 d까지, 또는 m 부터 p까지 중에 하나와 일치합니다: [a-dm-p] (합집합)
[a-z&&[def]] d, e, f 중의 하나와 일치합니다. (교집합)
[a-z&&[^bc]] b와 c를 제외한 a 부터 z까지 중의 하나와 일치합니다: [ad-z] (차집합)
[a-z&&[^m-p]] m부터 p 까지를 제외한, a 부터 z까지 중의 하나와 일치합니다: [a-lq-z] (차집합)

 

 

2. 미리 정의된 문자를 지정하는 방법입니다.

 

정규식 설명
. 임의의 문자 (라인 종결자와 일치할 수도 하지 않을 수도 있음)
\d 숫자 문자: [0-9]
\D 숫자 문자가 아닌것: [^0-9]
\s 화이트 스페이스 문자: [ \t\n\x0B\f\r]
\S 화이트 스페이스 문자가 아닌것: [^\s]
\w 알파벳 단어 문자(word 문자): [a-zA-Z_0-9]
\W 알파벳 단어 문자가 아닌것: [^\w]

 

 

3. 수량 매칭

 

패턴이 나오는 횟수를 지정할 수 있습니다.

 

Greedy Reluctant Possessive 의미
X? X?? X?+ X가 없거나 한번 나옴
X* X*? X*+ X가 없거나 한번 이상 나옴
X+ X+? X++ X가 한번 또는 여러번 나옴
X{n} X{n}? X{n}+ X가 정확히 n번 나옴
X{n,} X{n,}? X{n,}+ X가 n번 이상 나옴
X{n,m} X{n,m}? X{n,m}+ X가 n이상 m이하로 나옴

 

수량매칭은 각각 세 가지 방법을 사용할 수 있는데 조금식 차이가 있습니다.

 

- Greedy : 매칭을 위해서 입력된 문자열 전체를 읽어서 확인하고 뒤에서 한자씩 빼면서 끝까지 확인합니다.

- Reluctant : 입력된 문자열에서 한글자씩 확인해 나갑니다. 마지막에 확인하는 것은 전체 문자열 입니다.

- Possessive : 입력된 전체 문자열을 확인합니다. Greedy와 달리 뒤에서 빼면서 확인하지 않습니다.

 

 

4. 정규식 그룹 캡쳐

 

Pattern API에 설명된 대로, 그룹 캡쳐는 열린 괄호를 왼쪽에서 오른쪽으로 세어서 숫자를 붙입니다. 예로, 표현식 ((A)(B(C))) 는 다음과 같은 네 개의 그룹이 있습니다.

 

1: ((A)(B(C)))

2: (A)

3: (B(C))

4: (C)

 

이 그룹 번호는 역참조로 그룹에 접근하는데 사용될 수 있습니다.

 

 

5. 그룹 역참조

 

문자열내에서 중복된 알파벳으로된 단어를 찾습니다. \\1 로 번호가 붙은 부분이 1번 그룹을 역참조하는 부분입니다. 다음에 나올 대소문자를 무시하는 플래그(Pattern.CASE_INSENSITIVE)도 사용한 예제입니다.

 

String pattern = "\\b(\\w+)\\b[\\w\\W]*\\b\\1\\b";
Pattern p = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
String phrase = "unique is not duplicate but unique, Duplicate is duplicate.";
Matcher m = p.matcher(phrase);
while (m.find()) {
    String val = m.group();
    System.out.println("매칭된 하위 문자열: \"" + val + "\"");
    System.out.println("중복된 단어: " + m.group(1) + "\n");
}

결과)
매칭된 하위 문자열: "unique is not duplicate but unique"
중복된 단어: unique

매칭된 하위 문자열: "Duplicate is duplicate"
중복된 단어: Duplicate

 

 

6. 경계조건 판별 조건

 

행의 시작 끝, 단어 경계 등을 판별합니다.

 

정규식 설명
^ 행의 시작
$ 행의 끝
\b 단어 경계
\B 단어가 아닌것의 경계
\A 입력의 시작 부분
\G 이전 매치의 끝
\Z 입력의 끝이지만 종결자가 있는 경우
\z 입력의 끝

 

 

7. 패턴 매칭에서 플래그 사용

 

패턴이 매치되는 방법에 영향을 미치는 플래그를 제공합니다.

 

플래그 동등한 내장 표현 설명
Pattern.CANON_EQ None 표준화된 매칭 모드를 활성화합니다. 이 모드가 켜지면 a를 나타내는 유니코드 "\u00E5"와 a와 상단고리 유니코드를 쓴 "a\u030A"를 같다고 매칭합니다.
Pattern.CASE_INSENSITIVE (?i) 대소문자를 구분하지 않습니다.
Pattern.COMMENTS (?x) 공백과 주석이 무시됩니다. 주석은 #부터 그 행 끝까지 입니다.
Pattern.MULTILINE (?m) 다중행 모드를 사용여 모든 ^와 $가 인식됩니다. 기본값은 입력값 전체를 하나의 시작과 끝으로 인식합니다.
Pattern.DOTALL (?s) .가 개행문자 까지 포함하는 모든 문자로 매칭됩니다.
Pattern.LITERAL None 입력의 메타문자와 이스케이프된 문자를 일반 문자로 취급합니다. CASE_INSENSITIVE와 UNICODE_CASE는 기능이 유지됩니다.
Pattern.UNICODE_CASE (?u) 이 모드가 활성화 되면 대소문자 매칭이 유니코드 표준을 따릅니다. 기본은 US-ASCII 문자 집합을 따릅니다.
Pattern.UNIX_LINES (?d) ^와 $를 처리시 UNIX 개행을 사용합니다.

 

여러개의 플래그를 사용할 때는 | 연산자로 연결합니다.

 

플래그를 사용한 예제는 replaceAll() 메소드를 사용했습니다. 플래그를 정규식의 맨 앞에 나와야 합니다.

System.out.println("this\nis\ntest\n".replaceAll("(^.*$)", "A"));
System.out.println("this\nis\ntest\n".replaceAll("(?s)(^.*$)", "B"));
System.out.println("this\nis\ntest\n".replaceAll("(?m)(^.*$)", "C"));

결과)
B
C
C
C

 

참조 : http://docs.oracle.com/javase/tutorial/essential/regex/index.html

 

반응형