SSISO Community

시소당

JAVA에서의 정규식

정규  표현식(Regular  Expression)

정규  표현식은  텍스트와  데이터를  조작하는데  사용하는  강력한  문자열  처
리  도구이다.  주로  유닉스와  펄을  사용하는  사람들에게는  아주  익숙한  도구
이겠지만  자바에서는  그동안  지원하지  못하고  있었다.  따라서  자바  개발자
들은  별도의  다른  라이브러리를  구해서  사용하거나  직접  기능을  구현하는  
수고를  해왔으나,  1.4  버전부터는  정규  표현식  패키지가  추가되어  이러한  
번거로움을  덜  수  있게  되었다.

앞으로  정규  표현식을  사용하는  예제  프로그램들이  등장한다.  정규  표현식  
자체에  대한  자세한  사항이나  자바  프로그램의  기초에  대해서는  다른  서적
을  참고하기  바란다.  우리가  주목해야  할  것은  정규  표현식을  어떻게  자바
에서  사용하며,  어떤  예제들이  가능한지  살펴보는  것이다.  또한  예제들의  
구현은  정규  표현식을  사용할  경우와  사용하지  않을  경우를  비교할  수  있도
록  하였다.  

첫  번째  임무:  빈칸을  모두  제거하라!

주소록  프로그램을  작성한다고  상상해보자.  우리가  사용하는  우편번호
는  "123-456"  이런  형태로  이루어져  있다.  물론  중간에  있는  "-"  문자는  미
리  제거하거나  형식을  지정해서  입력  받으면  입력  오류를  상당히  줄일  수  
있다.  그러나  주소를  입력하던  도중  실수로  스페이스바를  잘못  눌러  빈칸
이  포함되게  되는  경우,  이  빈칸이  반드시  데이터에  필요한지  아니면  실수
로  입력  된  것인지  알기  어렵다.  빈칸  없이  입력되어야  하는  데이터에서는  
당연히  빈칸은  제거되어야  할  대상이다.  

본인이  수행하는  프로젝트에서는  다음과  같은  조건이  있었다.  
"입력되는  스트링은  통신  장비들  사이의  구성  관계  트리를  나타내는  값이
다"
"각  노드의  구분은  슬래시(/)로  구분된다"
"스트링  사이에  빈칸은  존재해서는  안된다"
예:  "cen99/con06/rnc06/bts00/BCCA00"
그런데  문제는  입력하는  사람들의  실수로  빈칸이  입력되는  경우가  많다는  
것이었다.  그래서  결국  빈칸을  없애는  코드를  다음과  같이  작성하게  되었
다.  

정규  표현식을  사용하는  경우
public  static  String  removeAllChar(String  in,  char  c)  {
                String  regex  =  "\\"    +  new  Character(c).toString();
                return  in.replaceAll(regex,  "");  //  since  JDK  1.4
}

처음부터  끝까지  문자  c로  매치되는  것을  스트링  ""으로  변환하는  코드이
다.  따라서  모든  문자  c가  제거되는  효과를  
나타낸다.  테스트  코드는  다음과  같다.  //  고의로  중간에  빈칸이  삽입된  스
트링
String  s1  =  "  cen99/con06  /rnc06/bts00/BCCA00  ";
String  r1  =  removeAllChar(s1,  '  ');
System.out.println("before  removeAllChar()  =>  ["  +  s1  +  "]");
System.out.println("after    removeAllChar()  =>  ["  +  r1  +  "]");

결과는  다음과  같은  형태로  나타난다.  …
before  removeAllChar()  =>  [  cen99/con06  /rnc06/bts00/BCCA00  ]
after    removeAllChar()  =>  [cen99/con06/rnc06/bts00/BCCA00]

정규  표현식을  사용하지  않는  경우
public  String  removeAllChar2(String  in,  char  c)  {
StringBuffer  buffer  =  new  StringBuffer();
                for  (int  i  =  0  ;  i  <  in.length()  ;  i++)  {
                        if  (in.charAt(i)  !=  c)  {
                                buffer.append(in.charAt(i));
                        }
                }
                return  buffer.toString();
}

정규  표현식을  사용하지  않고  StringBuffer  클래스를  사용하였다.  코드는  
좀더  복잡해  보이지만  결과는  동일하다.  

성능  비교

정규  표현식의  사용  유무는  결과만을  두고  비교할  때는  중요하지  않을  수
도  있다.  어차피  같은  입력을  넣었을  때  원하는  결과가  나온다면  만족스러
울  것이다.  그러나  어떤  코드가  더  빠르게  수행될  것인가?  같은  작업을  수
천번,  수만번  반복한다면  빠른  코드를  작성해야  하는  것이  당연한  이치일  
것이다.  두  경우를  간단히  성능  비교를  해본  결과를  살펴보자.  수행  시간
은  단순히  메소드  호출  전의  시간과  메소드  호출  후의  시간  차이이다.  정확
한  비교를  위해서는  다른  도구를  사용하거나  아주  많은  테스트를  거쳐야  하
겠지만  시간  관계상  2번씩  수행하고  평균을  낸  결과는  다음과  같았다.  코드
를  눈으로  보았을  때에는  정규  표현식을  사용하지  않은  경우의  코드가  복잡
하므로  훨씬  수행  시간이  길거라는  예상이었다.  그러나  결과는  ???  

[그림  1]  수행시간  비교  놀랍게도  결과는  정반대였다.  정규  표현식을  사용
한  간결한  코드가  더  오래  걸리는  것이다.  그래프를  간단히  설명하자면  다
음과  같다.  세로축은  메소드  수행시간을  밀리세컨드  단위로  측정한  것이
고,  가로축은  메소드의  입력으로  넘겨지는  스트링의  길이를  지정하는  for  
문의  인덱스(i)이다.  그래프  결과를  만든  소스  코드의  일부를  살펴보자.  

String  s1  =  "";
for  (int  i  =  0  ;  i  <  2000  ;  i++)  {
//  테스트를  위해서  일부러  빈칸을  넣어  보았다.
s1  =  s1  +  "  cen99/con06  /rnc06/bts00/BCCA00  ";  
}

startTime  =  System.currentTimeMillis();  //  시작  시간
String  r1  =  removeAllChar(s1,  '  ');
endTime  =  System.currentTimeMillis();  //  종료  시간
System.out.println(endTime  -  startTime);  //  수행  시간


입력되는  스트링의  값이나  길이,  표현식에  의해서  결과는  다르게  나올  수  
있다.  여러분도  다른  방법으로  성능을  비교하면서  결과를  예상해보기  바란
다.  

두  번째  임무  :  상위  경로를  제거하라!

우리가  사용하는  윈도우나  유닉스  등의  운영체제에서는  파일  시스템을  제공
한다.  영속적으로  보존해야  하는  모든  데이터는  파일에  저장해야한다고  할  
수  있으므로,  그만큼  파일은  중요하다.  따라서  모든  운영체제와  프로그램  
언어에서  파일  다루는  방법을  제공한다.  이번  예제는  이런  파일  시스템을  
다루는  기능의  일부로서  요구사항은  아주  간단하다.  
"파일의  절대  경로에서  상위  경로를  제외한  파일이나  디렉토리  자체의  이름
만을  가져와라"  즉,  "C:\wos\config\aokey\trace030217.log"와  같은  파일
의  경로가  있을  경우  원하는  결과는  "trace030217.log"  이다.  


정규  표현식을  사용하는  경우
1:  public  String  removeParentPath(String  in)  {
2:                  String  os  =  System.getProperty("os.name");
3:            String  regex  =  null;
4:                  if  (os.startsWith("SunOS"))  {  //  Solaris
5:                          regex  =  "^.*/";
6:                  }
7:                  else  if  (os.startsWith("Windows"))  {  //  Windows
8:                          regex  =  "^.*\\\\";
9:                  }
10:                else  {
11:                        //  Unknown  os
12:                        return  null;
13:                }
14:                in  =  in.replaceFirst(regex,  "");
15:                return  in;
16:  }

예제에  사용되는  정규  표현식에  대해  궁금하다면  참고  도서  『정규  표현식  
완전  해부와  실습(개정판)』의  274페이지를  참고하기  바란다.  주의  할  사항
은  5,  8번째  줄에서  운영체제에  따라  파일과  디렉토리를  구분하는  문자가  
다르다는  것이다.  실행을  위해서  아래와  같은  코드를  작성했다.  
String  s3  =  "C:\\wos\\config\\aokey\\trace030217.log";
String  r3  =  removeParentPath(s3);
System.out.println("before  =>  ["  +  s3  +  "]");
System.out.println("after  =>  ["  +  r3  +  "]");

결과는  다음과  같은  형태로  나타난다.  …
before  =>  [C:\wos\config\aokey\trace030217.log]
after  =>  [trace030217.log]

정규  표현식을  사용하지  않는  경우
public  String  removeParentPath(String  in)  {
                String  separatorChar  =  System.getProperty("file.separator");
                return  in.substring(in.lastIndexOf(separatorChar)+1  ,  
in.length());
}

이번에는  정규  표현식을  사용하지  않는  경우의  코드가  훨씬  간결해  보인
다.  물론  결과는  동일하다.  이번  예제의  성능  비교는  
여러분이  직접  해보길  바란다.  이번에도  정규  표현식을  사용하지  않은  경우
가  더  빠르리라  예상된다.  

마치면서

JDK  1.4에서  정규  표현식을  사용하는  예제를  간단히  살펴  보았다.  정규  표
현식은  워낙  훌륭한  도구이기  때문에  적절히  사용하면  아주  많은  분야에서  
활용이  가능하다.  실제로  프로젝트  수행에  사용되는  코드만을  예로  들었기  
때문에  나름대로  충실하리라  예상하지만  본인도  아직  정규  표현식을  많이  
사용해보지  못해서  여러분이  느끼기에  소개된  예제가  부족한  느낌이  있거
나  적절하지  못할  수  도  있다.  정규  표현식을  사용하는  경우와  사용하지  않
는  경우의  두  가지  예를  함께  성능  비교를  한  이유는,  여러분이  고정관념
에  사로잡히지  않았으면  하는  이유에서였다.  표현식,  입력되는  데이터  값,  
또는  주변  상황에  따라  비교  결과는  얼마든지  변할  수  있는  것이다.  단지  
여러분은  모든  방법을  알고,  빠른  방법을  선택하기만  하면  될  것이다.  

참고문헌
&#9702;  『정규  표현식  완전  해부와  실습(개정판)』&#9702;  
&#9702;  JDK  1.4  API  

[출처]  JAVA에서의  정규식|작성자  꿈꾸는자
http://blog.naver.com/idnabba?Redirect=Log&logNo=13283229

773 view

4.0 stars