# 정규표현식 (Regular Expression)
- 정규표현식(Regular Expression)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어이다.
- 정규표현식은 문자열을 처리할 때 특정 패턴을 찾거나 수정할 때 사용한다.
## 정규표현식의 기본 기호
| 기호 | 설명 | 예시 |
| --- | --- | --- |
| `.` | 임의의 한 문자를 의미한다. | `a.b`는 `aab`, `a3b`와 매치하지만, `ab`는 매치되지 않음 |
| `^` | 문자열의 시작을 의미한다. | `^a`는 "a로 시작하는 문자열"과 매치 |
| `$` | 문자열의 종료를 의미한다. | `a$`는 "a로 끝나는 문자열"과 매치 |
| `*` | 바로 앞의 문자가 0번 이상 반복됨을 의미한다. | `ab*`는 `a`, `ab`, `abbb` 등과 매치 |
| `+` | 바로 앞의 문자가 1번 이상 반복됨을 의미한다. | `ab+`는 `ab`, `abbb`와 매치하지만, `a`는 매치되지 않음 |
| `?` | 바로 앞의 문자가 0번 또는 1번 반복됨을 의미한다. | `ab?`는 `a`, `ab`와 매치 |
| `{n}` | 바로 앞의 문자가 n번 반복됨을 의미한다. | `a{2}`는 `aa`와 매치 |
| `{n,}` | 바로 앞의 문자가 n번 이상 반복됨을 의미한다. | `a{2,}`는 `aa`, `aaa`와 매치 |
| `{n,m}` | 바로 앞의 문자가 n번 이상, m번 이하 반복됨을 의미한다. | `a{2,4}`는 `aa`, `aaa`, `aaaa`와 매치 |
| `[]` | 대괄호 안의 어떤 문자라도 한 번 매치됨을 의미한다. | `[abc]`는 `a`, `b`, `c`와 매치되며, `a-c`로도 표현가능 |
| `-` | 문자 클래스 내에서 사용되어 범위를 나타낸다. | `[a-z]`는 소문자 알파벳 중 하나와 매치 |
| `\|` | 두 패턴 중 하나와 매치됨을 의미한다(OR 조건). | `a\|b`는 `a` 또는 `b`와 매치 |
| `\` | 이스케이프 문자, 특수 문자를 문자 그대로 매치하고자 할 때 사용한다. | `\.`는 `.` 자체와 매치 |
| `()` | 괄호 안의 문자를 하나의 단위로 취급한다. | `(ab)+`는 `ab`, `abab`등과 매치 |
| `\w` | 모든 단어 문자에 매치된다. 알파벳, 숫자, 밑줄(_)을 포함한다. | `\w`는 `a`, `1`, `_`등과 매치 |
| `\W` | 단어 문자가 아닌 문자에 매치된다. `\w`의 반대. | `\W`는 `!`, `@`, `#` 등과 매치 |
| `\d` | 모든 숫자에 매치된다. `[0-9]`와 동일. | `\d`는 `2`, `5`등과 매치 |
| `\D` | 숫자가 아닌 모든 문자에 매치된다. `\d`의 반대. | `\D`는 `a`, `!`등과 매치 |
| `\s` | 모든 공백 문자에 매치된다. 스페이스, 탭, 줄바꿈 등을 포함. | `\s`는 공백, `\t`, `\n` 등과 매치 |
| `\S` | 공백 문자가 아닌 모든 문자에 매치된다. `\s`의 반대. | `\S`는 `a`, `1`, `!`와 매치 |
## 정규표현식 사용 예시 - 이메일 검증
이메일 주소가 유효한지 검증하는 정규 표현식 예시는 다음과 같다

```regex
^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$
```
위 정규식은 다음과 같은 규칙을 가진다.
- `^[\w-\.]+`: 이메일 주소의 첫 번째 부분은 알파벳, 숫자, 밑줄, 하이픈, 마침표로 구성되어야 한다.
- `@`: 이메일 주소의 첫 번째 부분과 두 번째 부분을 구분하는 기호
- `([\w-]+\.)+`: 이메일 주소의 두 번째 부분은 알파벳, 숫자, 밑줄, 하이픈으로 구성되어야 하며, 마침표로 구분되어야 한다.
- `[\w-]{2,4}$`: 이메일 주소의 두 번째 부분은 알파벳, 숫자, 밑줄, 하이픈으로 구성되어야 하며, 길이는 2에서 4 사이여야 한다.
따라서 이 정규식은 `계정`, `@`, `도메인`, `.`, `최상위 도메인`의 형식을 가진 이메일 주소를 검증한다.
간단한 정규표현식 테스트는 [regexr.com](https://regexr.com/)에서 할 수 있다.
## 자바로 정규표현식 사용하기
자바에서는 크게 두 가지 방법으로 정규표현식을 사용할 수 있다.
1. `String` 클래스의 정규식 문법 사용
2. `java.util.regex` 패키지 사용
### `String` 클래스의 정규식 문법 사용
`String` 클래스에는 정규식을 사용할 수 있는 메서드가 있다.
| 메서드 | 설명 |
| --- | --- |
| `matches(String regex)` | 문자열이 정규식에 매치되는지 확인한다. |
| `replaceAll(String regex, String replacement)` | 문자열에서 정규식과 일치하는 부분을 다른 문자열로 대체한다. |
| `split(String regex)` | 문자열을 정규식에 맞게 분할한다. |
#### `matches(String regex)` 사용 예시
`String` 클래스의 `matches()` 메서드는 문자열이 정규표현식에 매치되는지 확인하는 메서드이다.
예를 들어 이메일 주소가 유효한지 검증하는 예시는 다음과 같다.
```java
String email = "test@gmail.com";
String regex = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$";
boolean isValid = email.matches(regex);
System.out.println(isValid); // true
```
#### `replaceAll(String regex, String replacement)` 사용 예시
`String` 클래스의 `replaceAll()` 메서드는 문자열에서 정규식과 일치하는 부분을 다른 문자열로 대체하는 메서드이다.
예를 들어 문자열에서 숫자만 추출하는 예시는 다음과 같다.
```java
String str = "Hello 123 World 456";
String regex = "\\d+";
String replaced = str.replaceAll(regex, "");
System.out.println(replaced); // Hello World
```
#### `split(String regex)` 사용 예시
`String` 클래스의 `split()` 메서드는 문자열을 정규식에 맞게 분할하는 메서드이다.
예를 들어 문자열을 공백으로 분할하는 예시는 다음과 같다.
```java
String str = "Hello World";
String regex = "\\s";
String[] splited = str.split(regex);
for (String s : splited) {
System.out.println(s);
}
// Hello
// World
```
### `java.util.regex` 패키지 사용
단순한 정규식을 사용할 때는 `String` 클래스의 정규식 문법을 사용해도 무방하지만, 반복적이고 복잡한 정규식을 사용할 때는 `java.util.regex` 패키지를 사용하는 것이 좋다.
왜냐하면 `java.util.regex` 패키지는 정규식을 컴파일하여 `Pattern` 객체를 생성하고, 이를 이용하여 문자열을 검색하거나 추출하는 등의 작업을 수행할 수 있기 때문이다.
#### `Pattern`과 `Matcher` 클래스
`Pattern` 클래스는 정규식을 컴파일하여 `Matcher` 객체를 생성하는 클래스이다. `Pattern` 클래스의 주요 메서드는 다음과 같다.
| 메서드 | 설명 |
| --- | --- |
| `static Pattern compile(String regex)` | 정규식을 컴파일하여 `Pattern` 객체를 생성한다. |
| `Matcher matcher(CharSequence input)` | 주어진 입력 문자열에 대한 `Matcher` 객체를 생성한다. |
| ... | ... |
`Matcher` 클래스는 정규식을 이용하여 문자열을 검색하거나 추출하는 클래스이다. `Matcher` 클래스의 주요 메서드는 다음과 같다.
| 메서드 | 설명 |
| --- | --- |
| `boolean matches()` | 문자열이 정규식에 매치되는지 확인한다. |
| `boolean find()` | 다음 매치를 찾는다. |
| `String group()` | 매치된 문자열을 반환한다. |
| ... | ... |
위 두 클래스를 이용하여 이메일 주소가 유효한지 검증하는 예시는 다음과 같다.
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String email = "test@gmail.com";
String regex = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(email);
boolean isValid = matcher.matches();
System.out.println(isValid); // true
}
}
```
문자열에서 숫자만 추출하는 예시는 다음과 같다.
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
String str = "Hello 123 World 456";
String regex = "\\d+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while (matcher.find()) {
System.out.println(matcher.group());
}
// 123
// 456
}
}
```
물론 이 외에도 `Pattern`과 `Matcher` 클래스는 더 많은 기능을 제공한다. 필요한 경우 해당 클래스의 API 문서를 참고하면 된다.
## 정규표현식 사용 시 주의사항
정규표현식은 아주 유용한 도구이지만, 사용 시 주의해야 할 사항이 있다.
- 가독성이 떨어진다.
- 주석이나 공백을 허용하지 않고, 여러 기호를 혼합해 사용하기에 가독성이 좋지 않다.
- 패턴이 복잡할 수록 실수가 많아진다.
- 프로그램마다 지원하는 정규표현식이 다르기 때문에 그에 맞춰 배우고 써줘야 한다.
- 정규식의 패턴이 복잡할수록 실행속도가 느려진다.
위와 같은 단점들이 있기 때문에 정규표현식 사용 시 주석을 달아 가독성을 높이고, 패턴이 복잡해지면 나눠서 사용하거나 메서드로 분리하여 사용하는 것이 좋다.
뿐만 아니라 정규표현식이 아닌 다른 방법으로도 문제를 해결할 수 있는지 고려해보는 것이 좋다.