정규표현식 (regular expression)

Agnes Park·2022년 2월 1일
0

JAVA

목록 보기
17/34
post-thumbnail

1. 정규표현식 기본

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

정규표현식       설명
^          문자열 시작
$          문자열 종료
.         임의의 문자 [단 ‘'는 넣을 수 없습니다.]
*         앞 문자가 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

2. 그룹 (group)

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

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

0개의 댓글