- 문자열 검색, 치환 등의 동작에 있어서 단순한 '문자열 비교' 를 하는 것이 아니라
특정 '패턴'과 비교하고자 할 때 이를 단 몇줄의 코드로 구현 가능!- 주어진 문자열에서 패턴을 찾아내는 것을 '패턴 매칭(pattern matching)' 이라 함
- 사용자가 입력한 문자열 패턴 유효성 체크 등에 많이 사용
ex) 주민등록번호, URL, email, 비밀번호,
날짜포맷(yyyy-mm-dd)
전화번호(010-xxxx-xxxx) ...- 자바는 java.util.regex 에서 관련 클래스들 제공
Pattern, Matcher ..
- 일반적인 작성단계
1) 주어진 정규표현식을 구현하는 Pattern 객체 생성
2) 패턴 매칭 수행객체 Matcher 생성
3) Matcher 객체로부터 패턴매칭을 수행하여 검색, 치환등의 동작
- 장점: 코딩량 저감, 거의 대부분의 언어에서 공용으로 사용.
- 단점: 처음에 배우기 어렵고, 코드 가독성 떨어뜨림.
- 정규표현식을 사용하는 String 메소드들:
matches(), split(), replaceAll(), replaceFirst()
- 정규표현식 연습 사이트 추천
: https://regexr.com/ (정규식 , 문자열 매칭 연습)
: https://regexone.com/ ( step by step 으로 연습 하기 좋음)
: https://regexper.com/ (특징: 시각화, 정규식을 이미지로 다운가능)
: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html (오라클 공식)
───────────────────────────────────────────
정규표현식 설명
^ 문자열 시작
$ 문자열 종료
. 임의의 문자 [단 ‘'는 넣을 수 없습니다.]
* 앞 문자가 0개 이상의 개수가 존재할 수 있습니다.
+ 앞 문자가 1개 이상의 개수가 존재할 수 있습니다.
? 앞 문자가 없거나 하나 있을 수 있습니다.
[] 문자의 집합이나 범위를 표현. -기호를 통해 범위를 나타낼 수 있음. ^가 존재하면 not을 나타냄.
{} 횟수 또는 범위를 나타냅니다.
() 괄호안의 문자를 하나의 문자로 인식합니다.
| 패턴을 OR 연산을 수행할 때 사용합니다.
\s 공백 문자
\S 공백 문자가 아닌 나머지 문자
\w 알파벳이나 문자
\W 알파벳이나 숫자를 제외한 문자
\d [0-9] 숫자
\D 숫자를 제외한 모든 문자
(?i) 대소문자를 구분하지 않습니다.
[정규표현식 사용예시1]
package com.lec.java.regexp01;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegExp01Main {
public static void main(String[] args) {
System.out.println("정규표현식 regular expression");
String input;
String regex;
Pattern pat; // import (패턴 객체 만들기)
Matcher matcher; // import (매칭 수행하는 matcher 객체)
System.out.println();
System.out.println("■ 정규표현식 객체, 메소드 연습");
System.out.println("패턴] . ← 임의의 문자 하나");
// 1).주어진 정규표현식을 구현하는 Pattern 객체 생성
// Pattern.compile(정규표현식 문자열) 사용
regex = "My...."; // My 로 시작하고 임의의 문자 4개가 뒤에 있는 패턴
pat = Pattern.compile(regex); // pattern 객체를 생성해서 리턴하여 pat 변수로 받음
input = "-My1234-"; // 패턴 매칭을 할 입력할 문자열
System.out.println("input: " + input);
// 2) 패턴 매칭 수행객체 Matcher 생성
// Pattern 의 matcher() 사용
// Pattern을 사용해서 주어진 문자열에서 패턴 매칭할 객체 생성 --> Matcher객체 리턴
// (아직 패턴 매칭을 진행하진 않았다)
matcher = pat.matcher(input); // 매칭된 결과를 다룰 수 있는 matecher 생성
// 3) Matcher 객체로부터 패턴매칭을 수행하여 검색, 치환등의 동작
// find() '다음' 패턴매칭 검색 , 패턴매칭 발견하면 true 아니면 false 리턴
// group() 바로 직전에 패턴매칭된 문자열 String 리턴 (찾아낸, 매칭된 문자열)
// reset() 다시 처음부터 패턴매칭하도록 reset 함.
// replaceFirst() : 첫번째 매칭을 치환
// replaceAll() : 모든 매칭을 치환
// matches() : 패턴매칭이 '문자열 전체영역' 이 패턴매칭 되는지 여부
// start() : 최근 매칭의 시작 index,
// end() : 최근 매칭의 끝 index (마지막 매칭된 문자 '다음' 인덱스값)
if(matcher.find()) { // 주어진 input에서 regex(pat)의 정규표현식을 찾음
System.out.println(matcher.group() + " {" + matcher.start() + "~" + matcher.end() + "}");
} else {
System.out.println("find() 실패");
}
// 위 코드를 다시 실행하면?? 다음 매칭을 시도한다(-My1234-까지 찾고 다음을 찾으려도함) -> 실패
if(matcher.find()) {
System.out.println(matcher.group() + " {" + matcher.start() + "~" + matcher.end() + "}");
} else {
System.out.println("find() 실패");
}
// reset() 다시 처음부터 패턴매칭하도록 reset 함.
matcher = matcher.reset();
// 다시 시도하면 매칭 성공
if(matcher.find()) {
System.out.println(matcher.group() + " {" + matcher.start() + "~" + matcher.end() + "}");
} else {
System.out.println("find() 실패");
}
// replaceFirst() : 첫번째 매칭 패턴을 치환하여 결과 리턴
System.out.println(matcher.replaceFirst("XXXX"));
// matches()
// 패턴매칭이 '문자열 전체영역' 이 패턴매칭 되는지 여부
System.out.println();
System.out.println("matches()");
matcher = pat.matcher("-My1234-");
if(matcher.matches()) {
System.out.println("matches() 매칭 OK");
} else {
System.out.println("matches() 매칭 FAIL");
}
matcher = pat.matcher("My1234");
if(matcher.matches()) {
System.out.println("matches() 매칭 OK");
} else {
System.out.println("matches() 매칭 FAIL");
}
// 위 코드를 아래와 같이 한 번에 만들 수도 있겠다.
if(Pattern.compile("My....").matcher("My1234").matches()) {
System.out.println("matches() 매칭 OK");
} else {
System.out.println("matches() 매칭 FAIL");
}
System.out.println();
System.out.println("Pattern.matches(regex, input) 사용");
// 단순히 '문자열 전체영역' 이 패턴에 맞는지 여부 만 확인하려면 간단하게 Pattern.matches() 사용하자.
// Pattern.matches()는 내부적으로 정확히 아래와 같이 동작하게 된다.
// Pattern.compile(regex).matcher(input).matches()
if(Pattern.matches("My....", "Myabcd")) {
System.out.println("Pattern.matches() 매칭 OK");
} else {
System.out.println("Pattern.matches() 매칭 FAIL");
}
System.out.println();
System.out.println("■ 여러개 패턴 검색");
// 과연 "My...." 으로 몇개가 매칭되나? : 예측해보자
// 기본적으로 대소문자를 구분하여 매칭한다
input = "-My98KK-myABCD--My1234567--MyZZ---My789"; // 3개 매칭
matcher = pat.matcher(input); // Matcher 생성
while(matcher.find()) {
System.out.println(matcher.group() + "{" + matcher.start() + "~" + matcher.end() + "}");
}
System.out.println();
System.out.println(matcher.replaceFirst("***"));
System.out.println(matcher.replaceAll("***"));
System.out.println();
System.out.println("find(fromIndex)"); // fromIndex부터 검색
matcher = pat.matcher(input);
int fromIndex = 16;
while(matcher.find(fromIndex)) {
System.out.println(matcher.group() + " {" + matcher.start() + "~" + matcher.end() + "}");
fromIndex = matcher.end(); // 패턴 발견 시, 그 뒤부터 다시 검색
}
System.out.println("\n프로그램 종료");
} // end main()
} // end class
[정규표현식 사용예시2]
package com.lec.java.regexp03;
import java.util.regex.Matcher;
public class RegExp03Main {
public static void main(String[] args) {
System.out.println("정규표현식\n");
String regex, intput, title;
String [] arrInput;
//─────────────────────────────────────────
title = "^ : 바로 문자뒤의 문자열로 시작됨";
regex = "^The"; // The로 시작하는 문자열 패턴
arrInput = new String[] {
"The Things", // 1 The
"On The Things", // 2 X
" The The The", // 3 X
"The The The" // 4 The
};
//─────────────────────────────────────────
title = "$ : 문자열의 마지막이 이 문자열로 마무리 됨";
regex = "Man$"; // Man 으로 끝나는 문자열
arrInput = new String[] {
"SuperMan",
"AquaMan",
"WonderWoman",
"WonderWoMan",
"PostMan "
};
//─────────────────────────────────────────
title = "^표현식$ : 정확하게 전체패턴매칭되는 문자열";
regex = "^SuperMan$"; // TODO
arrInput = new String[] {
"SuperMan",
"Super Man",
" SuperMan",
"SuperMan "
};
//─────────────────────────────────────────
title = " . : 어떤 문자든지 임의의 '한문자'를 말한다.꼭 하나의 문자와 매칭";
regex = "x.z";
arrInput = new String[] {
"xyz", // 1 xyz
"xxzdfdk", //2 xxz
"aa10x9zbxbz", // 3 x9z xbz
"xz", //4 안됨
"90x zxx_zdf", // 5 x z x_z
"xbz", // 6 xbz
"xyyz" // 7 안됨
};
//─────────────────────────────────────────
title = " * : 바로 앞의 문자가 없거나 한개 이상의 경우를 매칭";
regex = "ab*"; // a는 반드시 있어야 하고, b는 없거나 1개 이상 존재해야 함
arrInput = new String[] {
"a", // 1 a
"abc", // 2 ab
"ab", // 3 ab
"abbbaaaabababbab", // 4 abbb a a a ab ab abb ab
"bbba", // 5 a
"cdef" // 6 X
};
//─────────────────────────────────────────
title = " + : 바로 앞의 문자를 나타내면 꼭 한개 혹은 그 이상을 매칭";
regex = "ab+ "; // a는 반드시 와야 하고, b는 1개 또는 그 이상
arrInput = new String[] {
"a", // 1 X
"abc", // 2 ab
"ab", // 3 ab
"abbbaaaabababbab", // 4 abbb ab ab abb ab
"bbba", // 5 X
"cdef" // 6 X
};
//─────────────────────────────────────────
title = " ? : 바로 앞의 문자가 한개 있거나 없는것을 매칭";
regex = "ab?"; // a는 무조건 있어야 하고, b는 1개 또는 없어야 함
arrInput = new String[] {
"a", // 1 a
"abc", // 2 ab
"kkabcc", // 3 ab
"abbbaaaabababbab", // 4 ab a a a ab ab ab ab
"bbba" // a
};
//─────────────────────────────────────────
title = " [] : 안에 존재하는 문자들중 한 문자만을 매칭";
regex = "[abc]"; // a 또는 b 또는 c 중에 한문자에 매칭
arrInput = new String[] {
"able", // 1 2개
"bible", // 2 2개
"cable", // 3 3개
"xenosys", // 4 X
};
regex = "[abc]+";
// 1 ab
// 2 b b
// 3 cab
regex = "[a-z]+";
arrInput = new String[] {
"abc100", // 1 abc
"abcDefGHIUJ-KLM123opQrstuz" // 2 abc ef op rstuz
};
regex = "[a-zA-Z]+"; // a~z, A~Z
// 2 abcDefGHIUJ KLM opQrstuz
regex = "[a-zA-Z0-9]+"; // a~z, A~Z, 0~9
regex = "[a-zA-Z0-9-]+";
regex = "[0-9]+";
// ────────────────────────────────────────
title = " {} : 앞에 있는 문자나 문자열의 등장개수를 정함";
regex = "ab{2}"; // a로 시작하고 b가 두 번 등장하는 문자열
arrInput = new String[] {
"abb", // 1 abb
"abbb", // 2 abb
"abbbabbbbbbbbabaabab", // 3 abb abb
};
regex = "ab{2,}"; // b의 개수가 2개 이상
regex = "ab{3,5}"; // b의 개수가 3개에서 ~ 5개까지
//─────────────────────────────────────────
title = " () : ()안에 있는 글자들을 그룹화 ";
regex = "a(bc)*"; // (bc) 가 없거나 하나 이상
arrInput = new String[] {
"abc", // 1 abc
"abcbcbbac", // 2 abcbc a
"abcabcabc", // 3 abc abc abc
};
//─────────────────────────────────────────
title = " | : OR 연산자 역할";
regex = "a|b"; // a 또는 b 둘중 하나
arrInput = new String[] {
"a", // a
"b", // b
"ab", // a b
"xyz" // x
};
//─────────────────────────────────────────
title = "(?i) : 대소문자 구분안하고 매칭 "; // 타 언어 정규표현식과 다름
regex = "(?i)abc";
arrInput = new String[] {
"abc",
"Abc",
"ABC"
};
//─────────────────────────────────────────
title = "\\s : 공백, \\S : 공백아닌 문자"; // 공백 : 띄어쓰기, \n, \t, \r(캐리지 리턴) ...
regex = "\\s+";
arrInput = new String[] {
"Hello My World", // 2개
"He \tllo My World", // 3개
"\n\t Hello My World\n\n", // 4개
};
regex = "\\S+";
//─────────────────────────────────────────
title = "\\w : 알파벳이나 숫자, \\W 알파벳이나 숫자를 제외한 문자";
regex = "\\w+";
arrInput = new String[] {
"This is 2022-01-26"
};
regex = "//W+";
//─────────────────────────────────────────
title = "\\d : [0-9] 숫자, \\D 숫자를 제외한 모든 문자";
regex = "\\d+"; // TODO
arrInput = new String[] {
"Hello 2022-01-26 !!"
};
regex = "\\D+";
//─────────────────────────────────────────
title = "escaped character 매칭 시키기";
// regex = ".+"; // 이렇게 하면 전체 문자열이 매칭된다.
regex = "[.]+";
regex = "\\.+";
arrInput = new String[] {
"My name is.."
};
//*****************************************
// 패턴매칭 수행
System.out.println(title);
regExpTest(regex, arrInput);
System.out.println("프로그램 종료");
} // end main()
// 도우미 함수
public static void regExpTest(String regex, String [] arrInput) {
for(String input : arrInput) regExpTest(regex, input);
}
public static void regExpTest(String regex, String input) {
System.out.println("[정규표현식 매칭 테스트]-----------------");
System.out.println("정규표현식: " + regex);
System.out.println("입력문자열: " + input);
Matcher matcher = Pattern.compile(regex).matcher(input);
int groupCount = matcher.groupCount(); // 그룹 개수
int matchCount = 0;
while(matcher.find()) {
matchCount++;
System.out.println(" 매치" + matchCount + ": " + matcher.group() + " {" + matcher.start() + "~" + matcher.end() + "}");
// 그룹이 있으면 group별 출력
if(groupCount > 0) {
for(int i = 0; i <= groupCount; i++) { // i 범위 주목!
System.out.printf("\t group(%d): %s {%d~%d}\n",
i, matcher.group(i), matcher.start(i), matcher.end(i));
}
}
} // end while
if(matchCount == 0) System.out.println(" Ⅹ매치 없슴Ⅹ");
System.out.println();
} // end regExpTest()
} // end class
[정규표현식 연습문제]
<정규표현식 연습>
이번에 우리 쇼핑몰에서 할인 쿠폰을 발행하려 한다.
발행되는 쿠폰의 일련번호 형식은 다음과 같다.
알파벳두자리-숫자4자리-숫자3자리-알파벳3자리
- 알파벳은 대소문자 구문 없슴
- 숫자는 0으로 시작하면 안됨.
=> 사용자는 발급받은 쿠폰번호를 입력해야 하는데, 위와 같은 형식만 받아들일수 있도록 만들자
[허용예]
Ab-7890-786-zuy
ki-2010-893-Zip
[불가]
xX-1200-089-zuy
p9-324-389-zopl- 쿠폰번호를 계속해서 입력 받으면서 유효한 쿠폰입니다" 혹은 "유효한 쿠폰이 아닙니다" 판정결과를 출력
- 'quit' 입력하면 프로그램 종료
package com.lec.java.regexp04;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegExp04Main {
public static void main(String[] args) {
System.out.println("정규표현식 예제");
Scanner sc = new Scanner(System.in);
String regex;
regex = "^[a-zA-Z]{2}-[1-9][0-9]{3}-[1-9][0-9]{2}-[a-zA-Z]{3}$";
String input = "";
while(true) {
input = sc.nextLine();
if(input.equalsIgnoreCase("quit")) break;
if(Pattern.matches(regex, input)) {
System.out.println("유효한 쿠폰입니다");
} else {
System.out.println("유효한 쿠폰이 아닙니다");
}
}
sc.close();
System.out.println("프로그램 종료");
} // end main
} // end class
정규표현식에 ( ) 을 사용하여 패턴 내에서 '그룹'을 지정 하면 ( ) 의 개수만큼 그룹이 만들어진다
package com.lec.java.regexp02;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegExp02Main {
public static void main(String[] args) {
System.out.println("정규표현식 : group");
String input;
String regex;
Pattern pat;
Matcher matcher;
System.out.println();
regex = "(My)(....)"; // 정규표현식에 () 사용
pat = Pattern.compile(regex);
input = "-My98KK-myABCD--My1234567--MyZZ---My789";
matcher = pat.matcher(input);
System.out.println("groupCount(): " + matcher.groupCount() );
System.out.println("입력문자열: " + input);
while(matcher.find()) {
System.out.println(matcher.group() + "{" + matcher.start() + "~" + matcher.end() + "}"); // group(0)과 동일한 의미
// 그룹들 출력해보기
// group(int group), start(int group), end(int group)
System.out.println("\t group(0)" + matcher.group(0) + "{" + matcher.start(0) + "~" + matcher.end(0) + "}");
System.out.println("\t group(1)" + matcher.group(1) + "{" + matcher.start(1) + "~" + matcher.end(1) + "}");
System.out.println("\t group(2)" + matcher.group(2) + "{" + matcher.start(2) + "~" + matcher.end(2) + "}");
}
// 도우미 함수를 사용해보자
System.out.println();
regExpTest("My....", "-My1234-");
regExpTest("(My)(....)", "-My1234-");
regExpTest("My....", input);
regExpTest("(My)(....)", input);
System.out.println("프로그램 종료");
} // end main
// 도우미 함수
public static void regExpTest(String regex, String input) {
System.out.println("[정규표현식 매칭 테스트]-----------------");
System.out.println("정규표현식: " + regex);
System.out.println("입력문자열: " + input);
Matcher matcher = Pattern.compile(regex).matcher(input);
int groupCount = matcher.groupCount(); // 그룹 개수
int matchCount = 0;
while(matcher.find()) {
matchCount++;
System.out.println(" 매치" + matchCount + ": " + matcher.group() + " {" + matcher.start() + "~" + matcher.end() + "}");
// 그룹이 있으면 group별 출력
if(groupCount > 0) {
for(int i = 0; i <= groupCount; i++) { // i 범위 주목!
System.out.printf("\t group(%d): %s {%d~%d}\n",
i, matcher.group(i), matcher.start(i), matcher.end(i));
}
}
} // end while
if(matchCount == 0) System.out.println(" Ⅹ매치 없음Ⅹ");
System.out.println();
} // end regExpTest()
} // end class