SSISO Community

시소당

자바(java)에서 사용하는 정규표현식(Regular expression)

문자열에  대한  값을  체크  할  때  간단한  길이계산  정도야  if  (String.length()  >  0)  등을  이용해  작성할  수  있다.    하지만  복잡한  문자열(예를들어  주민등록  번호,  혹은  이메일  형식)등을  점검하고자  할땐  조건문이  꽤  복잡해  진다.

이런  경우를  대비해  java에선  '정규표현식(Regular  expression)을  1.4부터  제공한다.  


정규표현식?
문자열에  매치여부를  확인할  수  있고,  치환(replace)할  수  있는  문자열  형식
(필자  임의로  작성했습니다.  정확한  해석은  검색을  통해  ..)

그대로  해석하자면  '정규표현식'이란  문자열에  대한  형식이  있다.  그리고  우리가  검증하고자  하는  문자열이  '작성된  정규  표현식에  매치여부를  확인'할  수  있고,  특정  부분을  '치환할  수  있다'는  뜻이다.    
더  쉽게  보자면  이것은  '틀을  짜  놓고,  이  안에  들어가나?'를  보는  것  이다.  

그렇게  되면  복잡한  조건문을  통해  검증해야  하는  문자열도  정해진  식을  통해  단번에  매치여부를  확인할  수  있는  것이다.

이제  정규표현식에  사용되는  식을  살펴보자.


  정규식    설명  예제  
  
  .  임의의  한  문자(필수)를  의미  합니다.
  ab.(abc,  abd,abe)  ..  
  
  ?  바로  앞에  문자가  없거나  하나가  있음을  의미  합니다.  a?c  (ac,  abc,  bc)  ..  
  
  *  바로  앞에  문자가  없거나  하나이상  반복을  의미  합니다.  ab*  (a,  ab,  aaa)  ..  
  
  +  바로  앞에  문자가  하나이상  반복을  의미  합니다.  ab+  (ab,  abb,  abbb)  ..
  
  ^  문자열의  시작을  의미  합니다.  ^ab  (abc,  abcd,  abcde)  ..
  
  [^]  ^이후의  괄호안  형식을  제외함을  의미  합니다.  [^ab]cd  (ecd,  fcd,  gcd)  ..
  
$  
  문자열의  끝을  의미  합니다.  
  abc$  (pupu  abc,  story  abc)  ..
  
  []  []안의  형식  일치를  의미  합니다.    [abc]  (a,  b,  c,  ab,  bc,  abc)  ...
  
  {}  {}앞  문자열(혹은  문자)  반복  갯수를  의미  합니다.    ab{2}  (abab)  
ab{2,}  (2개이상)  
ab{1,2}  (1부터  2까지)
  
  ()  ()안의  내용  을  하나의  묶음으로  사용  함을  의미  합니다.    (ab){2}  (abab)
(ab)+  (ab,  abab,  ababab  ..)
  
  |  or연산을  의미  합니다.    (a|b|c)    (a,  b,  c,  ab,abc  ..)
  
  [0-9]  (부터  -  까지)의  숫자를  의미  합니다.    [0-9]  (0,  1,  2,  3,  4  ..)
  
[a-z]
  (부터  -  까지)의  소문자를  의미  합니다.    [a-z]  (a,  b,  c,  d  ..)
  
  [a-zA-Z]  (부터  -  까지)의  대,소문자를  의미  합니다.      [a-zA-Z]  (a,  b,  A,  B  ..)
  
  \p(Alpha)  대,소문자  아파벳을  의미  합니다.    (a,  b,  c,  D,  E,  F  ..)  
  
\p(Digit)  
  숫자를  의미  합니다.    (1,  2,  3  ..)
  
  \p{Alnum}  대,소문자  아파벳,  숫자를  의미  합니다.    (a,  b,  c,  d,  e,  1,  2,  3  ..)  
  
\d
  숫자를  의미  합니다.    (1,  2,  3,  4  ..)  
  
  \D  숫자가  아닌  것을  의미  합니다.    
    (a,  b,  d,  E  ..)
  

그냥  보기엔  복잡하기만  하다.  필자도  처음봤을땐  '외계어'로  보여서  도무지  손대고  싶지  않았다.  그런데  이게  ..  계속  왜  안되는지  이해를  못하면서  헤딩하다  보니  ..  '특별한  매력'을  느끼게  되었다.  --b  

우리가  처음  자바(그  외  다른  언어  등등)을  배울때  생각해  보자.  public  ?  static  ?  void  ?  main  ?  request  ?  response  ?  session  ?  어느것  하나  생소하지  않은것이  없었다.  영어는  어떠한가  ?  영어  알파벳을  처음  봤을  땐  '문자'  가  아닌  '기호'로  받아드리는게  맞는것  같다.  왜냐면  처음  보는거니까  ..  

이제  사용법을  살펴보자.  몇가지  예제를  살펴보면  더욱  쉽게  이해할  수  있다.

RegexSample  .java
package  pupustory.regex.sample;
public  class  RegexSample  {
public  static  void  main(String  ar[])  throws  java.io.IOException{
              //정규표현식  적용  a로  시작하며,  a다음  아무문자1개만,    마지막은c로  끝남
final  String  regex  =  "^a.c$";  
String  useStr  =  "a1c";
System.out.println(useStr.matches(regex));
}
}


별로  길지  않은  코드다.  regex에  형식을  지정하고  useStr의  내용이  맞는지  여부를  확인했다.  정규표현식은  자바에서  제공하는(1.4  이후)  java.util.regex.*  패키지가  있다.  그리고  String  에서도  손쉽게  사용할  수  있다.  만약  사용시  좀  더  강력한  정규표현식(예를들면  매치되는  문자열의  그룹이라던가  하는)을  이용하고자  할땐  패키지를  사용하는  것이  좋다.

이번엔  좀  더  난이도  있는  표현식을  살펴보자.

RegexSample  .java
package  pupustory.regex.sample;
public  class  RegexSample  {
public  static  void  main(String  ar[])  throws  java.io.IOException{
final  String  regex  =  "\\p{Alnum}+@\\p{Alnum}+\\.\\p{Alnum}+";
String[]  useStr  =  {"pupustory@gmail.com"
,"pupu한글@gmail.net"
,"pupu@gmail.net.net"
,"@.net"
};
for  (int  i=0;  i<useStr.length;  i++)  {
System.out.println(useStr[i].matches(regex));
}
}
}


이것은  이메일을  검증하는  코드다.  표현식이  좀  복잡해  보이지만  하나하나  살펴보자.

//  숫자,  혹은  영문자가  오게된다.  마지막에  +가  있으므로  반드시  하나  이상의  문자가  와야  한다.
\p{Alnum}+  
//  @문자가  온  뒤  위와  같은  형식이다.
@\p{Alnum}+
//  .문자가  온  뒤  위와  같은  형식이다.  '.'앞에  \가  온  이유는  정규표현식에서  '.'문자를  사용한다.
//  따라서  '이것은  정규표현식이  아닌  그냥  내가  원하는  특수  문자'임을  뜻한다.
\.\p{Alnum}+

결과는  처음  배열  문자만  통과되고  나머지는  false를  반환한다.  실제로  아주  쉽다.  단지  처음보는  문자열을  코드로  사용함에  있어  복잡해  보이는것은  사실이다.  이것은  점점  많은  예제를  통해  익숙해질  수  있다.  

이제  좀  더  고급  표현식을  사용하기  위해  패키지에  포함된  라이브러리를  이용해  보자.

RegexSample  .java
package  pupustory.regex.sample;
import  java.util.regex.*;
public  class  RegexSample  {
public  static  void  main(String  ar[])  throws  java.io.IOException{

//  정규표현식  형식  정의
final  String  regex  =  "\\p{Alnum}+@\\p{Alnum}+.\\p{Alnum}+";

//  정규표현식에  맞춰볼  문자열
String  useStr  =  "pupustory@gmail.com";

//  패턴  정의  (  주의할점은  인스턴스  생성이  아니라는  점이다.
//  실제  소스를  열어보면  다음과  같이  되어있다.
//  return  new  Pattern(regex,  0);
//  하지만  본  생성자는  private로  구성되어  있다.
Pattern  pattern  =  Pattern.compile(regex);

//  패턴에  대해  맞춰볼  문자열을  넘겨  Matcher  객체로  받는다.
Matcher  match  =  pattern.matcher(useStr);
//  패턴  일치사항을  발견할  경우  true를  반환한다.
System.out.println(match.find());
}
}

주석  내용만  살펴봐도  별  부담없는  소스다.  이제  패키지를  이용한  강력한  기능을  살펴보자.  만약  사용자가  txt형식의  큰  문서  파일을  받았다고  가정  하자.  그  문서  파일  중  '특정  규약에  맞는  문자열이  몇번이나  반복되어  나오는가?'를  분석하고자  한다.  우리가  지금까지  알아본  방법은  '임의의  하나  문자열이  지정된  형식에  맞는가?'에  대한  방법을  알아왔다.  즉.  '단건'에  대한  정보를  넘겨  그  '단건'정보가  일치하는지를  알아본  것이다.

큰  문서에  대해서  '얼마나  많이  지정된  패턴이  반복되는가?'를  알아보려고  한다면  어떻게  해야할까  ?  우리가  DAO등을  사용할  때  row를  가져오는  방법으로  루프돌린  방법은  다음과  같다.

//  other  code  ..
ResultSet  record  =  PreparedStatment.executeQery();
while(record.next())  {
//  order  code  ..
}
//  other  code  ..

간단히  위와같이  사용한다.  우리가  위에  살펴본  .find()메소드도  같은  역할이다.  정규표현식에  대해  문서를  검색하고,  매치되는  부분을  찾는다.  후에  다음  부분을  차자게되고  끝까지  찾아  더이상  없다면  false를  반환하는  것  이다.  이제  좀  복잡한  코드를  만들어  보자.

RegexSample  .java
package  pupustory.regex.sample;
import  java.util.regex.*;
public  class  RegexSample  {
public  static  void  main(String  ar[])  throws  java.io.IOException{
final  int  MAX_STRING_SIZE  =  1000;
java.util.Random  random  =  new  java.util.Random();
//  정규표현식  형식  정의
final  String  regex  =  "\\p{Alnum}+@\\p{Alnum}+.\\p{Alnum}+";
//  정규표현식에  맞춰볼  문자열
StringBuffer  useStr  =  new  StringBuffer();
//  정규표현식에  맞춰볼  문자열을  생성  합니다.

for  (int  i=0;i<MAX_STRING_SIZE;i++  )  {
useStr.append("pupustory"+i);
//  랜덤으로    @gmail.com을  추가합니다.  
//  @gmail.com이  추가되면  정규표현식에  맞으므로  후에  찾게  될  것  입니다.
//  추가되지  않으면  틀렸으므로  pass할  것  입니다.
if  (random.nextBoolean())  useStr.append("@gmail.com");
useStr.append("  ");
}
Pattern  pattern  =  Pattern.compile(regex);
Matcher  match  =  pattern.matcher(useStr);
//  매치된  문자열  갯수를  샙니다.
int  matchCount=0;
while(match.find())  {
System.out.println(match.group());
matchCount++;
}
System.out.println("정규표현식에  맞는  문자열  갯수  >  "  +  matchCount);
}
}

복잡해  보이지만  간단하다.  for문을  통해  반복하며  문자열을  만드는데  '@gmail.com'을  추가할지  안할지  정한다.  추가되면  정규표현식에  맞게되고,  그렇지  않으면  맞지  않게된다.  while(boolean)  돌며  매치되는  문자열을  검색하고,  더이상  내용이  없으면  false를  반환하므로  빠져나온다.

지금까지  살펴본  예제는  단순하다.  정규표현식이  어려운것은  바로  '처음  손대기가  까다로워  보인다'라는  것이  가장  큰게  아닌가  싶다.  표현식을  만드는게  복잡해  보이지만  익숙해지면  매력이  있고,  또  벨리데이션  체크에  있어  여러개의  조건이  들어갈  필요도  없다.  라이브러리  추가도  필요없다.  String에서  바로  사용하거나  패키지를  사용하면  된다.

필자도  처음에  어려워서  그만둘까  했지만  하다보니  정말  재미있어서  계속  빠지게  되었다.  이글을  보고  있고,  정규표현식에  관심있는  많은  개발자들이  허접한  이  글을  통해서  코드작성에  좀더  빠르게,  좀더  아름답게  하는데  보템이  되었으면  한다.

(group()메소드는  일치하는  그룹의  묶음이다.  차후  포스트  추가시  추가할  예정)

출처  :  http://pupustory.tistory.com/132

2563 view

4.0 stars