정규식은 문자열에서 특정 패턴을 찾거나 교체, 삭제 등 문자열을 원하는 형태로 편집하는 기능을 제공해 줍니다. 정규식은 자주 사용되지는 않지만, 어떤 경우에 일반적인 로직으로 처리하기 힘든것은 간편하게 처리할 수 있는 힘이 있습니다. 대부분의 언어들이 정규식을 가지고 있는데, 그 사용법들이 조금씩 다른것 같습니다.
자바 문자열 객체(java.lang.String)에서 정규식을 사용하는 자주 쓰는 메소드는 다음과 같습니다.
1
2
3
4
5
|
boolean matches(String regex)
String replaceAll(String regex, String replacement)
String[] split(String regex)
|
cs |
- matches(): 인자로 주어진 정규식에 매칭되는 값이 있는지 확인합니다.
- replaceAll(): 문자열내에 있는 정규식 regex와 매치되는 모든 문자열을 replacement문자열로 바꾼 문자열을 반환합니다.
- split(): 인자로 주어진 정규식과 매치되는 문자열을 구분자로 분할합니다.
정규식을 전문적으로 다루는 객체도 제공됩니다. java.util.regex 패키지에 있는 다음 세가지 클래스들 입니다. 주요한 차이점은 정규식을 Pattern 객체로 미리 컴파일 해둘수 있어서 처리 속도가 좀 더 빠르고, 매칭된 데이터를 좀더 상세히 다룰 수 있습니다.
1
2
3
|
- Pattern
- Matcher
- PatternSyntaxException
|
cs |
- Pattern 객체 : 정규식의 컴파일된 표현 입니다(정규식을 적용 가능하도록 컴파일해서 가지고 있습니다). Pattern 클래스는 공개된 생성자를 제공하지 않습니다. 패턴을 생성하려면 Pattern객체를 반환하는 정적 compile 메소드를 호출해야 합니다. 이 메소드는 첫 번째 인자로 정규식 문자열을 받아 들입니다.
- Matcher 객체 : 패턴을 해석하고 입력 문자열에 대해 일치 작업을 수행하는 엔진입니다. Pattern 클래스와 마찬가지로 Matcher는 어떤 공개된 생성자도 정의하고 있지 않습니다. Matcher객체는 Pattern 객체의 matcher 메소드를 호출해서 얻습니다.
- PatternSyntaxException : 객체 정규식 패턴의 문법 오류를 나타내는 unchecked 예외 입니다.
다음은 Pattern과 Matcher를 사용해서 문자열에서 패턴을 찾는 예제 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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());
}
}
|
cs |
텍스트 "<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 | 설명 |
A? | A?? | A?+ | A가 없거나 한 번 나옴 |
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)도 사용한 예제입니다.
1
2
3
4
5
6
7
8
9
|
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");
}
|
cs |
매칭된 하위 문자열: "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() 메소드를 사용했습니다. 플래그를 정규식의 맨 앞에 나와야 합니다.
1
2
3
|
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"));
|
cs |
B
C
C
'Study > etc' 카테고리의 다른 글
REST API URI 패턴 (0) | 2020.07.15 |
---|---|
서버리스(Serverless) 아키텍처 (0) | 2020.06.29 |