2013년 12월 31일 화요일

java 정규식에서 많이 사용하는 항목들(개인적으로...)

블로그에 글을 기재하기 시작한지 얼마 되지 않지만, 처음과는 다른각도에서 접근하게 되는것 같습니다.

개인적인 정리가 필요해서 시작했고, 지금도 그것이 중요한 이유이지만, 간혹 드물게 접속해서 보시는 분들이 있는 것 같습니다.
제가 검색등을 통해서 도움을 받았듯, 혹시라도 조금이라도 도움이 될 수 있다면 좋겠다는 생각을 아주 가끔(^^)은 하게 되었습니다.
조금은 생각을 정리해서 기재 해야 겠다는 생각이 들기 시작하는 중입니다. 

정규식은 Java 아닌 다른 곳에서도 많이 사용되는 표현 형식입니다.   
문장중의 특정 단어를 찾거나, 일부분을 변경하는 것 등에서도 많이 사용됩니다.  Linux 등의 환경에서는 일반정규식은 말할 것도 없고, 정규식이라 말하긴 어려워도 검색(파일 및 내용)을 위한 많은 명령어가 제공되기도 합니다. (awk, sed 등의 명령어는 정말 강력합니다. find 와 함께 쓰는 xargs 명령어도 파일 내용을 찾는 데에는 더할 나위 없이 강력하고요 ... ), Unix 에서의 egrep 는 정규식 사용할 수 있게 도와 줍니다.

java 에서는 java.util.regex package 정규식 관련한 내용이 담겨 있습니다.
처음 블로그 할 때 기록했던 수치 확인 내용을 다시 한번 기록해 보겠습니다.
[+\\-]?([\\d]+([.][\\d]*)?|[.][\\d]+)([eE][+\\-]?[\\d]+)? 
숫자형으로 넘어온 데이터가 DB 에서 문자열로 가져올 때, 가끔 형변환된 문자열이 다음과 같이 출력되어 나타날 수가 있습니다. -.7265  -1.2E+12 등의 문자열에서 -.으로 -0.7.. 형식이 아닌 데이터가 나타날 때도 수치형 데이터 라고 인식 시켜 주려면 +,- 기호가 사용가능한 
기호라는 표기가 필요합니다. 그게 [+\\-]? 기호 입니다. \\기호는 - 기호가 [] 블럭에서 이미 사용되고 있는 문자열이라서 의미 자체로 '-' 다 라는 것을 인식시켜 주기 위한 부분 입니다.  '?' 는 있을 수도 있고 없을 수도 있다라는 의미로 받아 들이시면 됩니다. 
정규식 API 를 참조하시면서 보시면 쉽게 확인 하실 수 있을 것 같습니다.  참고로 [\\d\\w]+ 라는 의미는 숫자 혹은 문자라는 의미 입니다. 

그럼 위의 내용을 어떻게 사용할까요 ?
위의 내용은 굳이 Patten 객체까지 사용하지 않고 String 객체에서 직접 사용하 실 수 있습니다.   아래와 같은 간단한 코드 입니다.
public static boolean isNumberFormatStr(String str) {
if ( str == null ){
return false;
}
return str.matches("[+\\-]?([\\d]+([.][\\d]*)?|[.][\\d]+)([eE][+\\-]?[\\d]+)?");
}

다음의 코드를 살펴보겠습니다.

public static void testRegex() {
StringBuffer sb = new StringBuffer();
sb.append("<a href=\"www.google.co.kr\" \r\n target=\"_black\"> TEST </a><a onClick=www.naver.com> test2\r\n</a>");

System.out.println( sb );

Pattern pObj = Pattern.compile("(<[^>]*>)",Pattern.MULTILINE);
Matcher mObj = pObj.matcher(sb);
while ( mObj.find()) {
System.out.println (mObj.groupCount() + " : " + mObj.group(0));
}

pObj = Pattern.compile("(<(/)?([\\w]+)([\\s]+[^>]*)?>)",Pattern.MULTILINE);
mObj = pObj.matcher(sb);
while ( mObj.find()) {
int cnt = mObj.groupCount();
for ( int i = 1; i <= cnt; i++ ) {
System.out.print ("\t\t" + i + " : " +  mObj.group(i));
}
}
}

html tag 를 확인하기 위한 코드 입니다.
처음에는 단순히 tag 만을 확인 하기 위해서 '<'가 있고 '>' 이 아닌 모든것인 있을 수도 있고 '>' 이 있는 유형을 찾도록 했습니다. <[^>]*> 가 그런 의미 입니다.
하지만 이렇게 되면 tag 안의 내용을 다시 확인하여야 합니다. 종료 tag인지 아닌지 이름이 뭔지 속성은 있는지 등등을요 그래서 (/)? 으로 종료 태그 여부를 확인합니다. 다시 태그 이름은 '<'다음에 붙어 나오는 단어이기 때문에 [\\w]+로 확인을 합니다.
그 다음은 속성에 해당하는 것이 있다면  확인하는 구문이 들어 있습니다.
()기호와 []기호 + ? * {1,5} 등의 기호로 찾아야할 영역을 지정할 수 있습니다.

아래의 내용을 한번 살펴 보겠습니다.
"ABCDEFGABCDEFGABCDEFG ABCDEFGABCDEFGABCDEFG" 라는 문장이 있습니다.
이 문장에서 EFG 가 포함된 영역과 그 외 영역을 찾고자 한다고 가정해 보겠습니다.
일반적으로 생각해 볼 수 있는 정규식은 (.+)(EFG)라는 문장을 생각해 볼 수 있습니다.
무엇이 되었던 EFG 앞의 내용은 다 그리고 EFG 를 찾아라 하는 구성입니다.
이것을 실행하면 ABCDEFGABCDEFGABCDEFG ABCDEFGABCDEFGABCD EFG 의 두 영역을 찾아 오게 됩니다.  원하는 내용이었다면 다행이지만 의도가 ABCD EFG 의 반복을 원하였다면 위의 정규식에서 (.+?)(EFG)의 구성으로 변경해 주시면 됩니다. +뒤의 ? 의 의미는 EFG 가 포함된 영역을 최소한의 수준에서 찾아 주게 됩니다.
API의 Greedy quantifiers, Reluctant quantifies 의 내용을 확인해 보시면 됩니다.
문장 전체에서 특정 영역을 찾아 오는 Parser를 구성할 때 위의 내용이 빈번하게 사용될 수 있습니다.(제 개인적으론 그렇습니다.)
코드로 구성해 보면 다음과 같습니다.

String str = "ABCDEFGABCDEFGABCDEFG ABCDEFGABCDEFGABCDEFG";
System.out.println ("\n\n" + str );
Pattern pObj = Pattern.compile("(.+?)(efg)",Pattern.CASE_INSENSITIVE);
Matcher mObj = pObj.matcher(str);
while ( mObj.find() ) {
System.out.println (mObj.group(1) + " : " + mObj.group(2) );
}


정규식은 mail 형식 html tag 수치확인 등의 단순한 확인 및 처리에서 부터 좀더 복잡한 파일내용의 분석 등 다양한 영역에서 사용할 수 있습니다.  정규식의 강력함을 접하면서 다 알지는 못하지만, 혹시 정규식을 많이 접하지 않았던 분들에게 조금이라도 도움이 될 까 해서 기록해 보았습니다.






댓글 없음:

댓글 쓰기