# 정규표현식 (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`, `!`와 매치 | ## 정규표현식 사용 예시 - 이메일 검증 이메일 주소가 유효한지 검증하는 정규 표현식 예시는 다음과 같다 ![image](https://hackmd.io/_uploads/rkCSRpegR.png) ```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 문서를 참고하면 된다. ## 정규표현식 사용 시 주의사항 정규표현식은 아주 유용한 도구이지만, 사용 시 주의해야 할 사항이 있다. - 가독성이 떨어진다. - 주석이나 공백을 허용하지 않고, 여러 기호를 혼합해 사용하기에 가독성이 좋지 않다. - 패턴이 복잡할 수록 실수가 많아진다. - 프로그램마다 지원하는 정규표현식이 다르기 때문에 그에 맞춰 배우고 써줘야 한다. - 정규식의 패턴이 복잡할수록 실행속도가 느려진다. 위와 같은 단점들이 있기 때문에 정규표현식 사용 시 주석을 달아 가독성을 높이고, 패턴이 복잡해지면 나눠서 사용하거나 메서드로 분리하여 사용하는 것이 좋다. 뿐만 아니라 정규표현식이 아닌 다른 방법으로도 문제를 해결할 수 있는지 고려해보는 것이 좋다.