JAVA

[JAVA] 정규표현식

shb 2022. 2. 17. 11:42

정규표현식 regular expression
  
  문자열 검색, 치환  등의 동작에 있어서 단순한 '문자열 비교' 를 하는 것이 아니라 
  특정 '패턴'과 비교하고자 할때 이를 단 몇줄의 코드로 구현 가능!
  주어진 문자열에서 패턴을 찾아내는 것을 '패턴 매칭(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)   대소문자를 구분하지 않습니다.

 * 자바 정규표현식에 사용되는 escaped character 들
    \.[]{}()<>*+-=!?^$|

public class RegExp01Main {

	public static void main(String[] args) {
		System.out.println("정규표현식 regular expression");
		
		String input;
		String regex;
		Pattern pat;
		Matcher matcher;
		
		System.out.println();
		System.out.println("■ 정규표현식 객체, 메소드 연습");
		System.out.println("패턴] .  ← 임의의 문자 하나");
		
		// 1).주어진 정규표현식을 구현하는 Pattern 객체 생성
		// Pattern.compile(정규표현식 문자열) 사용  

		regex = "My....";   // My 로 시작하고 임의의 문자4개가 뒤에 있는 패턴
		pat = Pattern.compile(regex);
		
		input = "-My1234-";
		System.out.println("input: " + input);
		
		// 2) 패턴 매칭 수행객체 Matcher 생성
		// Pattern 의 matcher() 사용
		// Pattern을 사용해서 주어진 문자열에서 패턴 매칭할 객체 생성 --> Matcher객체 리턴
		// (아직 패턴 매칭을 진행하진 않았다)
		matcher = pat.matcher(input);
		
		// 3) Matcher 객체로부터 패턴매칭을 수행하여  검색, 치환등의 동작  
		//  find() '다음' 패턴매칭 검색 , 패턴매칭 발견하면 true 아니면 false 리턴
		//  group() 바로 직전에 패턴매칭된 문자열 String 리턴
		//  reset() 다시 처음부터 패턴매칭하도록 reset 함.
		//  replaceFirst() : 첫번째 매칭을 치환
		//  replaceAll() : 모든 매칭을 치환
		//  matches() : 패턴매칭이 '문자열 전체영역' 이 패턴매칭 되는지 여부
		//  start() : 최근 매칭의 시작 index, 
		//  end() : 최근 매칭의 끝 index (마지막 매칭된 문자 '다음' 인덱스값)
		if(matcher.find()) {
			System.out.println(matcher.group() + " {" + matcher.start() + "~" + matcher.end() + "}");
		} else {
			System.out.println("find() 실패");
		}
		
		// 위 코드를 다시 실행하면??  다음 매칭을 시도한다 -> 실패
		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

그룹(group) 
 * 정규표현식에 () 을 사용하여  패턴 내에서 '그룹'을 지정 하면
 * () 의 개수만큼 그룹이 만들어진다.

package com.lec.java.regexp02;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/* 그룹(group) 
 * 정규표현식에 () 을 사용하여  패턴 내에서 '그룹'을 지정 하면
 * () 의 개수만큼 그룹이 만들어진다.
 */
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(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
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$";
		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  X
				"90x zxx_zdf",  // 5  x z  x_z
				"xbz",  // 6  xbz
				"xyyz"  // 7  X
		};
		
		//─────────────────────────────────────────
		title = " * : 바로 앞의 문자가 없거나 한개 이상의 경우를 매칭";
		regex = "ab*";
		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+";
		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?"; 
		arrInput = new String[] {
				"a",  // 1   a
				"abc",  // 2  ab
				"kkabcc",  // 3   ab
				"abbbaaaabababbab", // 4  ab a a a ab ab ab ab
				"bbba"  // 5  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' 입력하면 프로그램 종료

 

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}$";  // TODO
		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.regexp05;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/* 대표적인 정규 표현식 
 *  구글링 하면 대표적인 정규표현식들이 많이 구할수 있습니다.
 *  각 정규표현식들을 작성해보고
 *	매칭되는 문자열과 그렇지 않은 것들을 출력해봅시다.   
 */
public class RegExp05Main {

	public static void main(String[] args) {
		System.out.println("많이 쓰는 정규표현식");

		String regex, intput, title;
		String [] arrInput;
		
		//─────────────────────────────────────────
		title = "URL";
		regex = ""; // TODO
		arrInput = new String[] {
			// TODO
		};
		System.out.println(title);
		regExpTest(regex, arrInput);

		
		//─────────────────────────────────────────
//		title = "email";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
//		System.out.println(title);
//		regExpTest(regex, arrInput);

		//─────────────────────────────────────────
//		title = "주민등록번호";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
//		System.out.println(title);
//		regExpTest(regex, arrInput);
		
		//─────────────────────────────────────────
//		title = "날짜 YYYY-MM-DD";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
//		System.out.println(title);
//		regExpTest(regex, arrInput);
		
		//─────────────────────────────────────────
//		title = "자연수";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
//		System.out.println(title);
//		regExpTest(regex, arrInput);

		//─────────────────────────────────────────
//		title = "정수";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
//		System.out.println(title);
//		regExpTest(regex, arrInput);

		//─────────────────────────────────────────
//		title = "소수";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
//		System.out.println(title);
//		regExpTest(regex, arrInput);

		//─────────────────────────────────────────
//		title = "소숫점 둘째자리까지";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
//		System.out.println(title);
//		regExpTest(regex, arrInput);

		//─────────────────────────────────────────
//		title = "통화표시 (₩)";
//		regex = ""; // TODO
//		arrInput = new String[] {
//			// TODO
//		};
		
		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);

		if(Pattern.matches(regex, input)) {
			System.out.println("   ●매칭●");
		} else {
			System.out.println("   Ⅹ매칭 없슴Ⅹ");
		}
		
		System.out.println();
	} // end regExpTest()

} // end class

'JAVA' 카테고리의 다른 글

[JAVA] Wrapper 클래스  (0) 2022.02.17
[JAVA] 문자열 메소드  (0) 2022.02.17
[JAVA] 예외처리  (0) 2022.02.16
[JAVA] 다형성, 추상 클래스, 인터페이스  (0) 2022.02.09
[JAVA] 상속  (0) 2022.02.09