티스토리 뷰

기타 프로그래밍

정규표현식

LichKing 2017. 3. 9. 12:10

정규표현식은 나에게있어 암호같은 존재였다. 봐도 뭘 의미하는지도 잘 모르겠고... 당장 필요할때만 검색해서 패턴을 갖다쓰는 그런 존재였는데 이번에 정규표현식에 대해 알아봤다.

잘 쓰진못해도 내가 갖다쓰는 표현이 무엇을 의미하는지는 해석할 수 있을정도로는 알아야할것 같아서...


참고로 여기서 소개할 의미있는 표현들을 메타표현이라고 일컫는데 이 메타표현을 의미대로 사용하지않고 순수 문자 그 자체로 사용하기위해선 이스케이프(escape)해야한다. 앞에 역슬래쉬(\)를 붙여주면 된다.

또한 예제의 System.out.println();에 들어있는 문자열은 해당 패턴에 true임을 보장하는 문자열이다.


. : 문자 하나를 의미

public static void main(String[] arg) {
String pattern = "a.c";
System.out.println("abc".matches(pattern));
}


[] : 문자집합. 메타문자인 [] 안에 들어오는 패턴들은 하나의 문자에 대한 집합이다. 별거 아닌데 글로 설명하려니 너무 어렵다. 예제로 확인해보자.

public static void main(String[] arg) {
String pattern = "[abcdef][b][c]";
System.out.println("abc".matches(pattern));
}

가장 앞에 [] 가 사용됐고 안에는 abcdef 가 들어있다. []는 하나의 문자에 대응하므로 첫글자가 abcdef 중 하나여야한다. 마찬가지로 두번째는 글자는 b, 세번째 글자는 c 여야한다.


- : 범위를 표현한다. [] 와 응용하면 영어만 들어와야 한다거나 한글만 들어와야한다는 등의 체크가 가능하다.

public static void main(String[] arg) {
String pattern = "[a-zA-Z0-9]";
System.out.println("a".matches(pattern));
}


? : 앞에있는 표현의 문자가 1개이거나 없음을 체크한다.

String pattern = "[a-zA-Z0-9].?";
System.out.println("ab".matches(pattern));

첫문자는 영문 혹은 숫자이고 두번째 문자는 뭐가 와도 상관없는데 두번째 문자가 0개이거나 1개면 true를 리턴한다. 만약 ab가 아니라 abc로 패턴매칭시 false 리턴한다.


* : 앞에있는 표현의 문자가 0개 이상임을 체크한다. ?가 0개 혹은 1개라면 *는 무한하다.

public static void main(String[] arg) {
String pattern = "[a-zA-Z0-9].*";
System.out.println("abcdef".matches(pattern));
}

첫문자만 영문 혹은 숫자라면 항상 true를 리턴한다.


+ : ?나 *는 0개임을 표현하지만 +최소 1개 이상임을 의미한다.

public static void main(String[] arg) {
String pattern = "[a-zA-Z0-9].+";
System.out.println("abc".matches(pattern));
}


^ : 이 메타문자는 두가지 의미가있는데 패턴의 시작으로 사용되면 시작문자를 지정하는 의미이고, 문자집합 내에서 사용시 해당 집합은 제외함을 의미한다.

String pattern = "^[a-zA-Z0-9].*";
System.out.println("abcdef".matches(pattern));

첫문자는 영문 혹은 숫자여야한다.

public static void main(String[] arg) {
String pattern = "[^a-zA-Z0-9].*";
System.out.println("ㅇabcdef".matches(pattern));
}

첫문자는 영문, 숫자가 아니어야한다.


\w : 영문, 숫자, 밑줄(Underbar)를 의미한다. [a-zA-Z0-9_]의 줄임표현이라고 생각하면 된다. w를 대문자로 사용하면 반대임을 의마한다.

public static void main(String[] arg) {
String pattern = "[\\w]*";
System.out.println("abcdef".matches(pattern));
}
public static void main(String[] arg) {
String pattern = "[\\W]*";
System.out.println("안녕하세요 대문자로 씀".matches(pattern));
}


\d : 숫자를 의미한다. [0-9]의 줄임표현이라고 생각하면 된다. d를 대문자로 사용하면 반대임을 의미한다.

public static void main(String[] arg) {
String pattern = "[\\d]*";
System.out.println("123456".matches(pattern));
}
public static void main(String[] arg) {
String pattern = "[\\D]*";
System.out.println("안녕하세요 대문자로 씀".matches(pattern));
}


\s : 줄바꿈, 띄어쓰기 등을 의미한다.

public static void main(String[] arg) {
String pattern = "[\\s]*";
System.out.println("\n\n \t".matches(pattern));
}


{} : 앞에있는 표현의 개수를 지정한다. 가령 예를 들어 \w로 3개인 문자를 찾는다고 할때

public static void main(String[] arg) {
String pattern = "\\w\\w\\w";
System.out.println("abc".matches(pattern));
}

이런식으로 사용할 것이다. 3개일때야 저렇게 사용하지만 100개일땐 어떻게 해야할까? \w를 100번 써도되긴하지만 그럴때 {}를 사용할 수 있다.

public static void main(String[] arg) {
String pattern = "\\w{3}";
System.out.println("abc".matches(pattern));
}

지금은 인자로 3 하나만 줬지만 인자는 2개를 줄수있으며 최소, 최대 숫자를 줄수있다.

public static void main(String[] arg) {
String pattern = "\\w{1,3}";
System.out.println("ab".matches(pattern));
}

최소 1번에서 최대 3번 반복한다는 의미이다. 문자가 2개니 true를 리턴한다.

최소 2개 이상이고 최대는 지정하지않는다면 이런식으로 사용할 수 있다.

public static void main(String[] arg) {
String pattern = "\\w{2,}";
System.out.println("abcde".matches(pattern));
}

위에서 소개한 문자랑 같이 생각해보면 +는 {1,}, *는 {0,} ?는 {0,1} 인것으로 이해할 수 있을 것이다.


$ : 종료문자.

public static void main(String[] arg) {
String pattern = ".*f$";
System.out.println("abcdef".matches(pattern));
}


() : 문자집합을 그룹화할때 사용한다. 여태 소개한 메타문자들은 결국 문자 1개에 대응하는 것인데 여러개의 문자에 대응할땐 ()로 묶어서 사용한다. 또한 아래에서 살펴보겠지만 그룹화한 패턴에 대응하는 문자열을 일종의 변수 개념으로 참조할 수도 있는데 이런 기능때문에 캡쳐(capture)한다고 표현하기도 한다.

public static void main(String[] arg) {
String pattern = "(abc){3}";
System.out.println("abcabcabc".matches(pattern));
}


$1 : 그룹화한 문자열을 참조할때 사용한다. 프로그래밍언어마다 표현이 좀 다르며 자바에서는 $로 사용한다. 이것도 예제로 보는게 이해가 쉬울것이다.

public static void main(String[] arg) {
String pattern = "(abc)(def)(ghi)";
System.out.println("abcdefghi".matches(pattern));
}

그룹으로 묶였든 말든 어쨋든 이 표현은 true를 리턴하는게 맞다는건 이제 이해해야한다.


public static void main(String[] arg) {
String pattern = "(abc)(def)(ghi)";
System.out.println("abcdefghi".matches(pattern));
System.out.println("abcdefghi".replaceAll(pattern, "$1-$3"));
}

matches는 위에서 사용한것 그대로니 신경쓸 필요없고 replaceAll()의 두번째 인자를 보자. ()로 그룹화를 지은 차례대로 인덱싱이된다(1부터시작). -는 문자 그대로로 받아들이면되는데 즉, 1번째 그룹 + "-" + 3번째 그룹이 되게된다. 결과는 abc-ghi가 된다. 예를들어 -가 없는 숫자만의 전화번호에 -를 붙여줘야한다고 생각해보자.

public static void main(String[] arg) {
String pattern = "([\\d]{3})([\\d]{3,4})([\\d]{4})";
System.out.println("0111231234".replaceAll(pattern, "$1-$2-$3"));
System.out.println("01012341234".replaceAll(pattern, "$1-$2-$3"));
}

가운데 자리가 3자리인경우도 잡기위해 {3,4}로 인자를 넣었다. 반대로 -를 제거한다고 생각해보자.

public static void main(String[] arg) {
String pattern = "([\\d]{3})-([\\d]{3,4})-([\\d]{4})";
System.out.println("011-123-1234".replaceAll(pattern, "$1$2$3"));
System.out.println("010-1234-1234".replaceAll(pattern, "$1$2$3"));
}


'기타 프로그래밍' 카테고리의 다른 글

간결한 분기문 사용하기  (2) 2017.07.15
클로저(Closure)  (0) 2017.05.15
정규표현식  (0) 2017.03.09
프로시저, 함수, 서브루틴  (0) 2016.12.04
git, github 이용하기#2  (0) 2016.07.08
git, github 이용하기#1  (1) 2016.07.08
댓글
댓글쓰기 폼