정규표현식을 활용하는 방법을 잘 설명해 놓은 블로그이다.
https://zzang9ha.tistory.com/322
위 블로그에서 설명하는 것과 같이 카카오 문제를 해결해 보았다.
public class Regex {
public static void main(String[] args) {
String test = "...!@BaT#*..y.abcdefghijklm.";
// String test = "z-+.^.";
// String test = "=.=";
// String test = "123_.def";
// String test = "abcdefghijklmn.p";
System.out.println("test = " + test);
// 2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
String step2Regex = "[^a-zA-Z-_.]";
String step2 = test.replaceAll(step2Regex, "");
System.out.println("step2 = " + step2);
// 3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
String step3Regex = "[..]{2,}";
String step3 = step2.replaceAll(step3Regex, ".");
System.out.println("step3 = " + step3);
// 4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
String step4Regex = "^[.]|[.]$";
String step4 = step3.replaceAll(step4Regex, "");
System.out.println("step4 = " + step4);
// 6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
// 만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
String step6Regex = "[.]$";
String substring = step4.substring(0, 15);
String step6 = substring.replaceAll(step6Regex, "");
System.out.println("step6 = " + step6);
}
}
내친김에 자주 사용하는 정규 표현식에 대해서도 살펴보자.
정규 표현식 | 의미 |
^[0-9]*$ == \\d | 숫자 |
^[a-zA-Z]*$ | 알파벳 |
^[가-힣]*$ | 한글 |
^[a-zA-Z0-9] | 알파벳이나 숫자 |
^[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-z]+$ | 이메일(Email) |
\w+@\w+\.\w+(\\.\\w+)? | 이메일(Email) |
^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$ | 휴대폰 번호 |
이메일이다.
^[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-z]+$
정규 표현식 | 의미 |
^ | 뒤에 나오는 대괄호로 시작 |
[a-zA-Z0-9] | 영어 대소문자 또는 숫자로 이루어짐 |
+ | 1개 또는 그 이상이 존재해야 함 |
@ | @가 존재해야 하며 |
[a-zA-Z0-9] | 영어 대소문자 혹은 숫자 |
+ | 1개 이상 |
\\. | . (dot)이 있어야 하며 |
[a-z]+$ | 끝날때는 영어소문자 1개 이상이 있어야 한다. |
정규표현식을 자주 사용해보자.
https://zzang9ha.tistory.com/322
✔ 정규 표현식(Regular Expression)
안녕하세요, 이번에 정리할 내용은 Java에서의 정규 표현식(Regular Expression) 입니다.
최근 개발을 하면서 정규 표현식을 사용하면 더 간단하게 해결할 수 있는 문제들을 꽤 자주 접했었고,
그때마다 주먹 구구식으로 정확한 이해없이 검색을 통해 해결을 해왔었습니다...
따라서 이번에 정리를 한 번 하려고 합니다 ✏
위키백과에 나와있는 정규 표현식의 정의는 다음과 같습니다.
정규 표현식(正規表現式, 영어: regular expression, 간단히 regexp[1] 또는 regex, rational expression)[2][3] 또는 정규식(正規式)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어이다. 정규 표현식은 많은 텍스트 편집기와 프로그래밍 언어에서 문자열의 검색과 치환을 위해 지원하고 있으며, 특히 펄과 Tcl은 언어 자체에 강력한 정규 표현식을 구현하고 있다. 컴퓨터 과학의 정규 언어로부터 유래하였으나 구현체에 따라서 정규 언어보다 더 넓은 언어를 표현할 수 있는 경우도 있으며, 심지어 정규 표현식 자체의 문법도 여러 가지 존재하고 있다. 현재 많은 프로그래밍 언어, 텍스트 처리 프로그램, 고급 텍스트 편집기 등이 정규 표현식 기능을 제공한다. 일부는 펄, 자바스크립트, 루비, Tcl처럼 문법에 내장되어 있는 반면 닷넷 언어, 자바, 파이썬, POSIX C, C++ (C++11 이후)에서는 표준 라이브러리를 통해 제공한다. 그 밖의 대부분의 언어들은 별도의 라이브러리를 통해 정규 표현식을 제공한다. 정규 표현식은 검색 엔진, 워드 프로세서와 문서 편집기의 찾아 바꾸기 대화상자, 그리고 sed, AWK와 같은 문자 처리 유틸리티, 어휘 분석에 사용된다. ko.wikipedia.org/wiki/%EC%A0%95%EA%B7%9C_%ED%91%9C%ED%98%84%EC%8B%9D |
위에서 핵심 문장은 다음과 같습니다.
"특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어"
정규 표현식은 대부분 문자열과 관련된 문제 해결을 위해 사용합니다.
가장 많이 사용하는 예시가 이메일, 핸드폰 번호 등의 유효성 검사를 위해 사용합니다.
정규 표현식에 대해 문법과 예제를 통해 좀 더 자세히 살펴보도록 하겠습니다.
🎗 정규 표현식 문법
정규 표현식의 기호는 다음과 같습니다.
※ 2021.08.09 \w, \W 내용 수정
\w 의 경우 알파벳이나 숫자, 언더바(_) 기호를 찾습니다.
\W 의 경우 알파벳이나 숫자, 언더바(_)를 제외한 문자입니다.
public class Tests {
public static void main(String[] args) {
String str = "a_b-c";
System.out.println(str.replaceAll("\\W", ""));
}
}
따라서 언더바도 제외를 해야할 경우, 다음과 같이 작성을 해야 합니다. 😭
String str = "a_b-c";
System.out.println(str.replaceAll("[\\W_]", ""));
// System.out.println(str.replaceAll("[a-zA-Z0-9]", "")); 위와 동일한 결과
정규 표현식의 대부분은 위에 내용이 전부인데요, 위 내용을 이해하더라도 실제로 사용되는 정규식을 살펴보면
무슨 의미를 뜻하는지 이해하기 힘든 정규식이 몇몇 존재합니다... 🤦♂️
따라서 위 정규 표현식에서 더 나아가 실제 예제를 통해 살펴보겠습니다.
위 정규 표현식 기호에서 기억해야 할 점은, 백슬러시(\) 가 포함되어 있는 기호들입니다.
정규 표현식에서 백슬러시(\)는 확장 문자로, 다음에 일반 문자가 오면 특수문자로 취급하고, 백슬러시 다음에 특수문자가 오면 그 문자 자체를 의미합니다.
예를 들어, 알파벳이나 숫자를 나타내는 \w 같은 경우 Java에서 \ 자체는 특수문자로 취급하기 때문에, 알파벳이나 숫자를 판별하는 정규식 기호는 다음과 같이 작성을 해야합니다.
\\w // 알파벳이나 숫자
위 설명만 보고는 정확히 무엇을 의미하는지 이해하기가 힘들 수 있으므로, 아래에서 살펴볼 예제를 통해 이해하시면 될 것 같습니다 !!
정규 표현식 관련해서 너무나 다양한 기호들이 존재하기 때문에, 전부 다 외우는 것은 불가능하고,
어떠한 상황에 어떠한 기호가 사용되는지 이해하는 것이 중요한 것 같습니다.
개인적으로 주로 사용하는 기호들은 다음과 같습니다.
[] () \w * + {} ^ $ ...
자주 사용하는 패턴의 정규 표현식을 나타내보면 다음과 같습니다.
정규 표현식 | 의미 |
^[0-9]*$ == \\d | 숫자 |
^[a-zA-Z]*$ | 알파벳 |
^[가-힣]*$ | 한글 |
^[a-zA-Z0-9] | 알파벳이나 숫자 |
^[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-z]+$ | 이메일(Email) |
\w+@\w+\.\w+(\\.\\w+)? | 이메일(Email) |
^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$ | 휴대폰 번호 |
위와 같이 일반적으로 자주 사용되는 정규 표현식이 존재하는데요, 처음 봤을땐 각 표현식이 무슨 의미를 나타내는지 파악하기 힘들 수 있습니다.
예제를 통해 정규 표현식을 익힐 것을 추천드립니다 !!
🎗 정규 표현식 예제
예제에서 살펴볼 클래스 및 메소드는 다음과 같습니다.
클래스 및 메소드에 대한 자세한 내용은 공식 문서를 참고해주세요 !
docs.oracle.com/javase/8/docs/api/
Pattern 클래스
- 정규 표현식에 대한 문자열글 검증하는 기능
- 공개된 생성자를 제공하지 않음
- 패턴을 생성하려면 Pattern 객체를 반환하는 정적 compile 메소드를 호출해야 함
- matches() 메소드를 활용하여 검증
- regex: 정규 표현식
- input: 검증할 문자열
public static boolean matches(String regex, CharSequence input) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
return m.matches();
}
Matcher 클래스
- 패턴을 해석하고 입력 문자열에 대해 일치 작업을 수행하는 엔진
- Pattern 클래스와 동일하게 어떠한 공개된 생성자도 정의하고 있지 않음
- Pattern 객체의 matcher() 메소드를 호출해서 얻음
- matches() 메소드를 활용하여 검증
- regex: 정규 표현식
- input: 검증할 문자열
/**
* Creates a matcher that will match the given input against this pattern.
*
* @param input
* The character sequence to be matched
*
* @return A new matcher for this pattern
*/
public Matcher matcher(CharSequence input) {
if (!compiled) {
synchronized(this) {
if (!compiled)
compile();
}
}
Matcher m = new Matcher(this, input);
return m;
}
String.replaceAll()
- Java의 문자열에서 정규식을 사용하는 메소드 중 하나입니다.
- 문자열 내에 있는 정규식(regex)와 매칭되는 모든 문자열을 replacement 문자로 치환합니다.
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
저는 String 클래스의 replaceAll() 메소드를 통해 정규 표현식이 어떻게 사용되는지 살펴보겠습니다 !!
Pattern 클래스 및 Matcher 클래스에 대한 사용법은 아래 블로그를 참고해 주세요 😊
blog.naver.com/zzang9ha/222013925468
coding-factory.tistory.com/529
🎗String.replaceAll(String regex, String replacement)
예제는 2021 카카오 블라인드 채용 코딩 테스트의 문제인 신규 아이디 추천 입니다.
위와 같이 아이디의 유효성을 검사하는 여러 단계가 존재하는데요,
각 단계를 정규 표현식을 통해 처리를 하면 간단하게 해결할 수 있습니다.
위 문제에서 정규 표현식을 통해 해결할 수 있는 단계는 2단계, 3단계, 4단계, 6단계 정도 인 것 같습니다.
하나씩 살펴봅시다😎
🎶 2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
위에서 핵심 키워드는 알파벳 소문자, 숫자, 빼기, 밑줄, 마침표 입니다.
정규 표현식은 알파벳 소문자(a-z), 숫자(0-9 혹은 \d), 빼기&밑줄&마침표(-_.) 입니다.
따라서 2단계를 정규 표현식을 통해 모든 문자를 제거하는 코드는 다음과 같습니다.
// String s
s = s.replaceAll("[^a-z\\d\\-_.]*", "");
- ^의 경우 문자열의 시작을 의미하지만, [] 내에 존재하면 not 을 의미합니다.
- 위에서 설명드렸듯이, 백슬러시(\)를 두 번 써주어야 정상적인 검사가 가능합니다.
위 정규 표현식을 더 간단하게 줄이면 다음과 같습니다.
s = s.replaceAll("[^\\w\\-_.]*", "");
- 2단계의 유효성 검사에는 일치하지 않지만, 문제의 1단계에서 이미 모든 문자를 소문자로 치환했기 때문에 위와 같이 \w(알파벳이나 숫자)를 작성해주어도 결과는 동일합니다.
- 위 정규 표현식은 문자열에 대문자가 존재하면, 대문자는 제거해주지 않습니다.
🎶 3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
위에서 핵심 키워드는 2번 이상 연속된 부분입니다.
위의 유효성 검사는 정규 표현식을 사용하지 않고 가능하겠지만, 코드가 복잡하게 될 거라고 생각합니다...🤔
따라서 위 문제를 해결하기 위해 횟수 또는 범위를 나타내는 {} 기호를 사용합니다.
// "[.]{2.}"
s = s.replaceAll("\\.{2,}", ".");
- 주석 처리된 정규 표현식과 replaceAll에 작성된 정규 표현식을 동일한 의미입니다.
- \\. (마침표)
- {2,} 2회 이상
🎶 4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
위에서 마침표(.)가 처음이나 끝에 위치한다면 제거를 해야하는데요, 만약 정규 표현식을 사용하지 않는다면 ..
첫 문자와 마지막 문자가 마침표(.) 인지 여부를 판단해서 제거해야 합니다.
하지만, 정규 표현식을 통해 간단히 해결이 가능합니다.
s = s.replaceAll("^[.]|[.]$", "");
- ^ 는 문자의 시작을 의미합니다.
- [.] 는 . 을 의미하고, 즉 ^[.] 는 문자열이 마침표(.)로 시작하는 것을 의미합니다.
- | 는 패턴 안에서 or 연산(처음이나 끝에 위치하는 경우)을 의미합니다.
- [.]$ 는 문자열의 끝이 마침표(.)로 끝나는 것을 의미합니다.
2022.02.07 추가
- 위 정규 표현식은 대괄호를 생략하고다음과 같이 나타낼 수 있습니다.
s = s.replaceAll("^.|.$", "");
🎶 6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다. 만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
위에서 마침표(.)가 끝에 위치하는 경우는 4단계에서 작성을 해보았습니다.
따라서 6단계의 전체 검사를 나타내는 코드는 다음과 같습니다.
private KakaoId notGraterThan16() {
if (s.length() >= 16) {
s = s.substring(0, 15);
}
s = s.replaceAll("[.]$", "");
return this;
}
이상으로 자바 정규 표현식에 대해 살펴보았습니다!
정규 표현식은 정말 자주 사용하는 것은 아니지만, 알아두면 꼭 사용해야 할 때가 있는 것 같습니다.
따라서 정규 표현식에 대해 어느정도 이해하고, 써먹을 수 있을 정도로는 공부를 하는 것이 좋을 것 같습니다.
Replace와 ReplaceALL, ReplaceFirst 에 대한 설명은 아래 블로그에서 확인하실 수 있습니다.
https://coding-factory.tistory.com/128
String변수나 배열 같은 곳에 많은 양의 데이터들이 들어가 있을 경우 자신이 바꾸고자 하는 값만 골라서 바꾸기란 쉽지 않습니다. 이럴 때 유용하게 쓰일 수 있는 함수가 바로 Replace함수입니다. 이번 포스팅에서는 문자열 치환 함수 Replace, ReplaceAll, ReplaceFirst 함수의 사용법에 대해서 알아보겠습니다.
Replace
String a = "무궁화 삼천리 화려강산 대한사람 대한으로 길이 보전하세 ";
//replace([기존문자],[바꿀문자])
a= a.replace("대한", "민국");
System.out.println(a);
//결과값 : 무궁화 삼천리 화려강산 민국사람 민국으로 길이 보전하세
String replace(CharSequnce target, CharSequence replacement)
Replace 함수는 자신이 바꾸고싶은 문자로 문자열을 치환시켜주는 기능을 합니다.
ReplaceALL
String a = "무궁화 삼천리 화려강산 대한사람 대한으로 길이 보전하세 ";
//replaceAll([정규식],[바꿀문자])
a= a.replaceAll("대한", "민국");
System.out.println(a);
//결과값 : 무궁화 삼천리 화려강산 민국사람 민국으로 길이 보전하세
ReplaceAll 함수는 자신이 바꾸고싶은 문자로 문자열을 전부 치환시켜주는 기능을 합니다. 여기서 많은 분들은 Replace와 ReplaceAll과의 같은 것이 아니냐고 반문을 할 수도 있을 듯합니다. 실제로 나오는 결과물의 값이 같습니다. 하지만 차이점은 분명 있습니다. Replace는 첫 번째 값으로 바꿀 문자열을 입력받는 대신 첫 번째 인자 값으로 정규식이 들어갑니다. 그래서 Replace는 특수문자로도 치환이 되는데 반하여 ReplaceAll은 특수문자로 치환이 어렵습니다.
Replace와 ReplaceALL의 차이점
String a = "무궁화. 삼천리. 화려강산. 대한사람. 대한으로. 길이. 보전하세 ";
//replace([기존문자],[바꿀문자])
a = a.replace(".", "/");
System.out.println(a);
//결과값 : 무궁화/ 삼천리/ 화려강산/ 대한사람/ 대한으로/ 길이/ 보전하세
. 을 /로 치환할 경우 replace함수는. 을 /로 정상적으로 바꾸어주었지만
SString a = "무궁화. 삼천리. 화려강산. 대한사람. 대한으로. 길이. 보전하세 ";
//replaceAll([정규식],[바꿀문자])
a = a.replaceAll(".", "/");
System.out.println(a);
//결과값 : /////////////////////////////////////
replaceAll 같은 경우 문자열 전체가 치환되는 것을 확인할 수 있습니다.
.(마침표)가 정규식으로 모든 문자를 의미하기 때문에 나타난 현상입니다.
ReplaceFirst
String a = "무궁화 삼천리 화려강산 대한사람 대한으로 길이 보전하세 ";
//replaceFirst([기존문자],[바꿀문자])
a= a.replaceFirst("대한", "민국");
System.out.println(a);
//결과값 : 무궁화 삼천리 화려강산 민국사람 대한으로 길이 보전하세
ReplaceFirst 함수는 자신이 바꾸고 싶은 문자열이 처음으로 해당할 때만 치환시켜주는 기능을 합니다.