본문 바로가기

JAVA 개인공부

[JAVA] 정규표현식(regex) [1/2]

정규표현식이란

정규표현식(Regular expressions)은 줄여서 Regex라고 합니다.

정규표현식의 사전적인 의미로는 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어입니다.

 

Regex는 문자열에 어떤 패턴의 문자들이 있는지 찾는데 도움을 줍니다.

입력한 문자열에서 특정한 조건을 표현할 경우 일반적인 조건문은 다소 길어질 수 있습니다.

그러나 정규표현식을 이용하면 매우 간단하게 표현할 수 있습니다.

 

단, 그만큼 코드가 간결하여 숙지하지 않으면 이해하는 데에 어려움이 있습니다.

정규표현식의 Metacharacters

Meta 문자열(Metacharacters)는 Regex의 패턴에서 어떤 문자가 특별한 의미를 갖는 것을 말합니다.

특별한 의미를 지니는 문자는 '[]'와 같은 문자이며, 이들을 Meta 문자라고 합니다.

 

자주 사용되는 Meta 문자를 정리하면 아래와 같습니다.

 

Meta 문자 의미 사용 예시
^ 문자열의 시작을 알림 ^regex$
$ 문자열의 종료를 알림
(?1) 대소문자를 구분하지 않음 (?!)[AB] : A, B, a, b 중 1개 허용
[ ] 안에 있는 지정문자 여부를 검사함
^를 붙이면 NOT을 의미함
[ABC] : A, B, C 중 1개 허용
[A-F] : A-F 사이의 1개 허용
[^0-5] : 0-5 사이의 숫자가 아닌 문자 1개 허용
{ } 범위 지정 {1} : 1개만 허용
{2,} : 2개 이상 (0~1개는 안됨)
{3,5} : 3~5개 허용
( ) [ ] 과 달리 안의 문자열을 하나로 인식 (ABC) : "ABC" 1개 허용
. \를 제외한 모든 문자를 의미 .[A] : 문자 아무거나 하나와 A 1개 허용
* 없거나 있음 [A-F]* : 아예 없거나 A-F 사이 문자가 하나 이상 있음
+ 하나 이상 있음
{1,} 과 동일
[A-F]+ : A-F 사이 문자가 하난 이상 있어야 함
? 없거나 1개 허용
{0, 1}과 동일
[A-F]? : 아예 없거나 A-F 사이 문자가 1개만 있어야 함
| 또는 (or) (C++ | JAVA) : "C++" 또는 "(" 1개 허용
\d 숫자일 것 [\\d]{2,5} : 숫자를 2~5개 허용
\D 숫자가 아닐 것 [\\D]{2,5} : 숫자가 아닌 것 2~5개 허용
\w 문자 또는 숫자(공백, 특수문자 등 제외) [\\w[2,5} : 문자 또는 숫자 2~5개 허용
\W 문자 또는 숫자가 아님(공백, 특수문자 등) [\\W{2,5} : 문자나 숫자가 아닌 것 2~5개 허용
\s 공백 [\\s]{3} : 공백이 3개여야 함
\S 공백이 아님 [\\S]{3} : 공백이 아닌 문자가 3개여야 함
\b 단어의 경계(공백)를 찾음 "\\bis\\b" : 양 옆으로 공백이 있는 문자열 is를 찾음

 

※ 역슬래시(\) 사용법

1. 위치마다 인식하는 문자의 범위가 조금 다르긴 하지만 편하게 공통적으로 생각하는 것이 좋음

 

2. 메타문자에 사용되는 문자를 제외한 모든 문자는 그냥 검사해야 할 문자로 인식

    - [A!@#]은 'A', '!', '@', '#'을 허용

    - (A!@#)은 "A!@#"을 허용

 

3. 역슬래시(\)가 붙어있는 메타문자를 사용할 때는 앞에 역슬래시(\)를 하나 더 붙여줘야 함

    - (\\s) : 공백 하나만 허용

    - 하나만 붙이면 이스케이프 문자(escape charactor)로 인식

    - [\t]로 사용하면 "\t"(탭)을 true로 인식(필요하면 사용 가능)

 

4. 특수문자 앞에 \\를 붙이면 특수문자 자체를 검사할 문자로 인식

    - [\\!] : '!' 하나만 허용('!'는 메타 문자가 아니므로 [!]로 써도 무관)

 

5. 메타문자를 제외한 일반 문자 앞에는 \\를 안 쓰는 게 좋음

    - [\\f] : 에러는 안 나지만 검색이 안 됨

 

6. 역슬래시 자체를 정규표현식의 검사할 문자로 인식시키려면 "\\\\"로 써주면 됨

    - [\\\\] : '\' 하나만 허용

    - 검사할 문자열에서 \를 표현하고 싶으면 \\ 두번 사용하면 됨

    - "\\" : '\' 문자 하나를 의미

정규표현식 작성 방법

정규표현식을 작성하는 방법은 자바 API java.util.regex 패키지를 사용합니다..

자바에서 정규표현식을 사용할 때에는 java.util.regex 패키지 안에 있는

Pattern 클래스와 Matcher 클래스를 주로 사용합니다.

 

Pattern 클래스

정규 표현식에 대상 문자열을 검증하는 기능은

java.util.regex.Pattern 클래스의 matches() 메소드를 활용하여 검증할 수 있습니다.

 

정규 표현식은 "\d"와 같이 String으로 표현할 수 있습니다.

Pattern은 컴파일된 정규 표현식이라고 합니다.

 

검증 후 대상 문자열이 정규표현식과 일치하면 true, 그렇지 않으면 false를 리턴합니다.

 

import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        String pattern = "^[0-9]*$";	// 숫자만 허용하는 regex
        String val = "123456789"        // 대상 문자열
        
        // mathces(정규표현식, 대상 문자열)
        boolean regex = Pattern.mathces(pattern, val);
        System.out.println(regex);
    }
}

// 실행결과
true

 

다음과 같이 정규 표현식(Regex) String을 컴파일하여 Pattern이라는 객체를 생성해서 사용해도 됩니다.

 

Pattern pattern = Pattern.compile("\\bcat\\b");

 

Matcher 클래스

Matcher 클래스는 대상 문자열의 패턴을 해석하고 주어진 패턴과 일치하는지 판별할 때 사용합니다.

 

Matcher 클래스의 입력값으로는 CharSequence라는 새로운 인터페이스가 사용되는데

이를 통해 다양한 형태의 입력 데이터로부터 문자 단위의 매칭 기능을 지원받을 수 있습니다.

 

Matcher 객체는 Pattern 객체의 matcher() 메소드를 호출하여 받아 올 수 있습니다.

 

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

public class RegexExample {
    public static void main(String[] args) {
    
        // compile(String regex) : 주어진 정규표현식으로부터 패턴을 생성
        Pattern pattern = Pattern.compile("^[a-zA-z]*$"); // 영문자만
        String val = "abcdef"; // 대상 문자열
        
        Matcher matcher = pattern.matcher(val);
        System.out.println(matcher.find());
    }
}

// 실행결과
true

 

위 예제는 Matcher 클래스의 find() 메소드를 활용하여 대상 문자열이 영문자인지 검증하는 것입니다.

대상 문자열이 영문자가 맞다면 true를 그렇지 않다면 false가 출력됩니다.

 

Matcher를 통해서 찾은 패턴은 다음과 같이 결과를 출력할 수 있습니다.

start()는 첫 번째 문자의 index, end()는 마지막 문자의 index를 리턴합니다.

group()은 일치하는 문자열을 리턴합니다.

 

public void Example() {
    Pattern pattern = Pattern.comile("\\bcat\\b");
    Matcher matcer = pattern.matcher("cat cat cat cattie cat");
    
    int count = 0;
    while(matcher.find()) {
        count++;
        System.out.println("Match number " + count);
        System.out.println("group(): " + matcher.group());
        System.out.println("start(): " + matcher.start());
        System.out.println("end(): " + matcher.end());
    }
}

// 실행결과
Match number 1
group(): cat
start(): 0
end(): 3
Match number 2
group(): cat
start(): 4
end(): 7
Match number 3
group(): cat
start(): 8
end(): 11
Match number 4
group(): cat
start(): 19
end(): 22

 

정규표현식 Metacharacters 예제

패턴에서 "."는 문자 1개를 의미합니다. 그 문자가 숫자인지 공백인지는 중요하지 않습니다.

아래 예제에서 패턴 "ab."는 "ab"와 일치하지 않습니다.

"ab" 뒤에 문자가 없기 때문입니다.

 

public void ex1() {
    String pattern = "ab.";		// ab와 \를 제외한 다른 문자로 구성되어야 함
    assertFalse("ab".matches(pattern));
    assertTrue("abc".matces(pattern));
    
    pattern = "ab\\s\\S";		// ab와 공백과 공백이 아닌 다른 문자로 구성되어야 함
    assertFalse("ab  ".matches(pattern));
    asserTrue("ab c".matches(pattern));
}
예제는 Junit을 이용하여 작성되었습니다. assertTrue()는 결과로 true가 리턴된다는 것을 의미하며 assertFalse()는 결과로 false가 리턴된다는 것을 의미합니다.

^는 문자열의 시작지점을 찾습니다. 따라서 ^ 다음으로 오는 패턴으로 문자열이 시작되는 것을 찾습니다.

아래 예제에서 repalceAll(regex, replacement)은 regex와 일치하는 내용을 replacement로 교체합니다.

 

public void ex2() {
    String result;
    result = "The cat sat on the mat.".repalceAll("[Tt]he", "*");
    System.out.println(result);
    
    result = "The cat sat on the mat.".repalceAll("^[Tt]he", "*");
    System.out.println(reuslt);
}

// 실행결과
* cat sat on * mat.
* cat sat on the mat.

 

&는 문자열의 종료지점을 찾습니다.

따라서 & 앞의 패턴으로 문자열이 끝나는 것을 찾습니다.

 

public void ex3() {
    String result;
    result = "The cat sat on the mat. and the cat".repalceAll("cat", "*");
    System.out.println(result);
    
    result = "The cat sat on the mat. and the cat".repalceAll("cat$", "*");
    System.out.println(result);
}

// 실행결과
The * sat on the mat. and the *
The cat sat on the mat. and the *

 

\b는 단어의 경계선을 찾는 역할을 합니다.

다음 예제를 보면 경계선의 의미가 무엇인지 이해할 수 있습니다.

단어 양 옆에 \b를 사용하여 다른 문자와 결합되지 않은, 독립적인 단어를 찾을 수 있습니다.

 

public void ex4() {
    String result;
    result = "This island is beautiful.".replaceAll("is", "*");
    System.out.println(result);
    
    result = "This island is beautiful.".replaceAll("\\bis\\b", "*");
    System.out.println(result);
}

// 실행결과
Th* *land * beautiful.
This island * beautiful.

 

[ ]는 내부의 문자열과 일치하는 문자 1개를 찾습니다.

"-"를 사용하여 범위를 지정할 수 있습니다.

 

public void ex5 {
    String pattern = "[abc][vz]";        // a, b, c 중 하나의 문자와 v, z 중 하나의 문자로 구성되야 함
    assertTrue("av".matches(pattern));
    assertFalse("ac".mathces(pattern));
    
    pattern = "Ex_[a-g1-5]";             // a ~ g 중 하나의 문자 or 1 ~ 5 중 하나의 숫자
    assertTrue("Ex_g".matches(pattern));    // 지정한 범위에 해당하는 문자 g
    assertFalse("Ex_6".matches(pattern));   // 지정한 범위를 벗어나는 숫자 6
}

 

다음은 숫자, 문자, 공백을 표현하는 Metacharacters들에 대한 예제입니다.

 

public void ex6() {

    // \d : 0~9 사이의 숫자
    // \D : 숫자가 아닌 어떤 문자
    String pattern = "\\d\\D";
    assertTrue("1a".matches(pattern));
    assertFalse("a1".matches(pattern));
    
    // \s : 공백 문자 하나
    pattern = "\\d\\s\\D";
    assertTrue("1 a".matches(pattern));
    
    // \S : 공백을 제외한 어떤 문자
    pattern = "\\d\\s\\S\\D"; 
    assertTrue("1 1a".matches(pattern));
    
    // \w : Alphanumeric(alphabet, 숫자) answk
    // \W : Alphanumeric을 제외한 어떤 문자(공백 문자 등)
    pattern = "\\w\\W";
    assertTrue("1 ".matches(pattern));
}

'JAVA 개인공부' 카테고리의 다른 글

[JAVA] 정규표현식(regex) [2/2]  (0) 2021.06.30