정규 표현식
정규표현식(Regular Expression)
혹은 Regex
란 특별한 의미를 갖는 문자, 숫자, 기호등을
조합해 만들어낸, 특정 문자열과 매칭되는 패턴을 의미한다.
Regex Grammar
정규식의 모든 문법을 외워야 할 필요는 없다. 아래 표의 기초 문법을 익히고, 필요할 때 찾아보도록 하자.
Java Regex 에서 모든 문법을 확인 할 수 있다.
Group, Range
Sign | Meaning |
---|---|
| | or |
( ) | group |
(?: ) | non capturing group |
[ ] | any character in char set |
[^ ] | except char set |
Quantifiers
Sign | Meaning |
---|---|
* | 0 or more |
+ | 1 or more |
? | 0 or 1 |
{n} | n times |
{n, } | at least n times |
{n, m} | n ~ m |
Boundary
Sign | Meaning |
---|---|
^ | beginning of sentence |
$ | end of sentence |
\b | beginning or end of word |
\B | middle of word |
Character classes
Sign | Meaning |
---|---|
a-Z, 0-9, etc | alphabet, digit and other non meta character itself |
\ | escape |
. | any character except \n |
\d | digit |
\D | not digit |
\w | alphanumeric and underscore |
\W | not alphanumeric and underscore |
\s | space |
\S | not space |
Meta character ?
정규식에서는
. * + ? { } ( ) [ ] | \ ^ $
와 같은 문자들을 특별한 용도로 사용하고 있기 때문에, 문자로서 이런 특수문자 들을 사용하려면 앞에 ` \ ` 를 붙여야 한다. 예를 들어\*
는 반복 횟수를 나타내지 않는*
그 자체이다.Easy regex example
- 숫자
^[0-9]*$
^
와$
는 정규식의 시작과 끝을 의미한다.[0-9]
는0, 1, 2, ..., 9
을 의미한다.*
s는 앞의 문자가 없거나, 무한히 많은 경우를 의미한다. 따라서^[0-9]*$
는 숫자로만 구성된 문자열을 의미한다.
- 영문자
^[a-zA-Z]*$
- 한글
^[가-힣]*$
Regex in Java
regex
는 문자열이 정해진 형식으로 구성되어져 있는지 검증 하거나, 특정 형식(전화번호, 이메일)을 만족하는 문자를 찾기 위해 사용된다. Java는 정규식을 사용하기 위해 Pattern
과 Matcher
클래스를 지원해주고 있다.
주어진 문자열이 특정 형식을 준수하는지는 Pattern
클래스의 matches
메서드를 이용한다.
String regex = "^[0-9]*$"; // 숫자만
String input1 = "123456";
String input2 = "12345a";
boolean result1 = Pattern.matches(regex, input1); // true
boolean result2 = Pattern.matches(regex, input2); // false
텍스트로 부터 규칙을 만족하는 문자를 모두 찾으려면 정규식을 패턴화 시킨 후, Matcher
객체를 생성해 사용한다.
String regex = "[a-zA-Z]+"; // 알파벳만
String input = "..."
Pattern pattern = Pattern.complie(regex);
Matcher matcher = pattern.matcher(input);
List<String> result = matcher.results()
.map(MatchResult::group)
.collect(Collectors.toList());
위 코드는 input
안에 존재하는 알파벳으로만 구성된 단어들을 리스트 형태로 추출한다.
e.g. 이메일, 전화번호 추출하기
String input = "This is the sample text for regex practice. " +
"it contains some email addresses and phone numbers. " +
"Email addresses contain at sign(@), like hello@naver.com, someone@hello.co.kr. " +
"The phone number could have a hyphen like 017-1234-5678, or not. " +
"Some people use the dot instead of hyphen, like 02.123.4567 " +
"or just use the space like 010 1234 4321."
String emailRegex = "[a-zA-Z0-9.]+@[a-zA-Z0-9.]+";
String phoneNumRegex = "\\d{2,3}[- .]\\d{3,4}[- .]\\d{4}";
Pattern emailPattern = Pattern.compile(emailRegex);
Matcher emailMatcher = emailPattern.matcher(sample);
List<String> emailList = emailMatcher
.results()
.map(MatchResult::group)
.collect(Collectors.toList());
Pattern phoneNumberPattern = Pattern.compile(phoneNumRegex);
Matcher phoneNumberMather = phoneNumberPattern.matcher(sample);
List<String> phoneNumList = phoneNumberMather
.results()
.map(MatchResult::group)
.collect(Collectors.toList());
System.out.println("emailList : " + emailList);
System.out.println("phoneNumList : " + phoneNumList);
/*
emailList : [hello@naver.com, someone@hello.co.kr]
phoneNumList : [017-1234-5678, 02.123.4567, 010 1234 4321]
*/
Group
정규식과 매칭되는 항목의 일부분 만을 이용하고 싶을 때는 그룹을 이용한다.
String phoneNumRegex = "(\\d{2,3})[- .](\\d{3,4})[- .](\\d{4})";
Pattern phoneNumberPattern = Pattern.compile(phoneNumRegex);
Matcher phoneNumberMather = phoneNumberPattern.matcher(sample);
List<String> phoneNumList = phoneNumberMather
.results()
.map(m -> m.group(1))
.collect(Collectors.toList());
System.out.println(phoneNumList)
// [017, 02, 010]
Replace
정규식와 매치되는 특정 항목들의 값을 변경하고 싶을 때는, replaceAll
메서드를 사용한다.
e.g. 이메일과 전화번호를 감춰보자
String input = "This is the sample text for regex practice. " +
"it contains some email addresses and phone numbers. " +
"Email addresses contain at sign(@), like hello@naver.com, someone@hello.co.kr. " +
"The phone number could have a hyphen like 017-1234-5678, or not. " +
"Some people use the dot instead of hyphen, like 02.123.4567 " +
"or just use the space like 010 1234 4321."
String regex = "[\\w-.]+@[a-zA-Z0-9.]+|\\d{2,3}[- .]\\d{3,4}[- .]\\d{4}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(sample);
String result = matcher.replaceAll(" **** ");
System.out.println(result);
/*
This is the sample text for regex practice. it contains some of email addresses and phone numbers.
Email addresses contain @, like **** , **** The phone number could have hyphen like **** , or not.
Some people use the dot like **** or use just space like ****
*/
Flag
플래그는 정규 표현식이 작동하는 방식을 바꾸기 위해 사용한다.
Pattern.CASE_INSENSITIVE
ori:
대소 문자를 구분하지 않도록 변경한다.Pattern.MULTILINE
orm:
^ &
가 전체 입력이 아닌, 각 줄의 시작과 끝에 일치하도록 변경한다.Pattern.DOTALL
ors:
.
이 개행 문자\n
을 포함하도록 변경한다.
String regex = "aBc";
String input = "...";
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITVE);
boolean isMatch = pattern.matcher(input).find();
위와 같이 CASE_INSENSITIVE
플래그를 적용하면, 정규식은 대소문자를 구분하지 않으므로 aBc, aBC, ABc, ABC, AbC
등의 문자를 input
으로 사용해도 pattern.matcher(input).find()
메서드는 true
를 반환한다.
플래그를 아래와 같이 정규식에 포함시킬 수도 있다. 이때는 (?<flag>:<regex>)
와 같은 문법을 사용한다.
String regex = "(?i:aBc)";
String input = "...";
boolean isMatch = Pattern.matches(regex, input)
Exercise
e.g 2021 카카오 : 신규 아이디 추천 문제 풀어보기
public class Main {
public static void main(String[] args) {
String id = "...!@BaT#*..y.AdeFGhiJ.kLm";
System.out.println("step 0 :" + id);
// step 1. 모든 대분자를 대응되는 소문자로 치환한다.
id = id.toLowerCase();
System.out.println("step 1 :" + id);
//step 2. 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거
id = id.replaceAll("[^a-zA-Z0-9_.\\-]", "");
System.out.println("step 2 :" + id);
// step 3. 마침표(.)가 2번 이상 연속된 부분은 하나의 마침표로 치환한다.
id = id.replaceAll("\\.+", ".");
System.out.println("step 3 : " + id);
// step 4. 마침표(.)가 처음이나 끝에 위치하면 제거한다.
id = id.replaceAll("^\\.|\\.$", "");
System.out.println("step 4 : " + id);
// step 5. id가 빈 문자열 이라면, id 에 "a" 를 대입한다.
if(id.isBlank()) id = "a";
System.out.println("step 5 : " + id);
// step 6-1. id의 길이가 16자 이상이면, 첫 15자를 제외한 나머지 문자를 모두 제거한다.
if(id.length() >= 16) {
id = id.substring(0, 15);
}
System.out.println("step 6-1 : " + id);
// step 6-2. 15번째 문자에 마침표(.)가 오는 경우 삭제
id = id.replaceAll("\\.$", "");
System.out.println("step 6-2 : " + id);
// step 7. id의 길이가 2자 이하라면, 3글자가 될 때까지 마지막 문자를 더해준다.
while(id.length() < 3) {
id = id + id.charAt(id.length()-1);
}
System.out.println("step 7 : " + id);
}
/*
step 0 : ...!@BaT#*..y.AdeFGhiJ.kLm
step 1 : ...!@bat#*..y.adefghij.klm
step 2 : ...bat..y.adefghij.klm
step 3 : .bat.y.adefghij.klm
step 4 : bat.y.adefghij.klm
step 5 : bat.y.adefghij.klm
step 6-1 : bat.y.adefghij.
step 6-2 : bat.y.adefghij
step 7 : bat.y.adefghij
*/
}
정규식 연습 사이트 에서 연습해보는 것을 적극 추천한다.
댓글남기기