잡동사니

정규표현식을 사용해보자..

어진동그라미 2006. 7. 7. 12:27
예전에 뉴스그룹에 올렸던 내용을 옮겨 놓습니다.. 뉴스그룹에서 의견교환시 FAQ 대용으로 활용하기 위해서..

주민등록번호, 전화번호, 우편번호, 이메일주소, IP 주소, 숫자만 입력을 받아야 하는 경우 등등 문자열을 입력받아 이를 점검해야 하는 경우가 많이 있습니다.. 뭐.. 자주 쓸 것이니 이것만 입력 받도록 해주는 에디트 컨트롤을 만드는 것도 방법이겠습니다만.. 이런 경우 저런 경우 변종에 변종.. 컴포넌트 만드는 것도 쉬운일이 아닙니다..
이제 앞으로 이런 검사들은 모두 정규표현식을 사용해 검사를 하는게 어떨까 하는 생각이 듭니다.. 아울러 검색, 치환 등에도 탁월한 편리함을 제공하죠..

이미 많은 웹 개발자들은 적극 활용을 하고 있더군요..

그도 그럴 것이 VBScript와 자바 스크립트에서는 정규표현식을 지원하는 완벽한 기능을 제공합니다.. 아주 사용하기 편리하기 때문에 별로 걱정할게 없죠..
function autolink_function(str)
   dim reg

   set reg = New RegExp
   reg.pattern =
       "(http|ftp):\/\/([a-z0-9\_\-\./~@?=%&:\-]+)"
   reg.Global = True
   reg.IgnoreCase = True
   str = reg.Replace(str,
       "<a href='$1://$2' target=new>$1://$2</a>")

   reg.pattern =
       "([a-z0-9\_\-\.]+)@([a-z0-9\_\-\.]+)"
   str = reg.Replace(str,
       "<a href='mailto:$1@$2'>$1@$2</a>")

   autolink_function = str
end function
위의 코드는 HTML 문서, 혹은 문자열을 뒤벼서 http나 ftp 주소, 혹은 이메일 주소에 하이퍼링크 태그를 달아주는 함수입니다.. 파싱할 필요도 없고, 문자열 검색할 필요도 없습니다.. 치환함수에 정규표현식을 넣어주면 그 문자열 패턴에 맞는 문자열들은 알아서 지가 찾아 다 변환시켜주는 것을 볼 수 있습니다.. 정말 환상입니다..

자바 스크립트에서 정규표현식을 사용하는 방법은 아래 링크를 참조..
http://devedge.netscape.com/library/manuals/2000/javascript/1.3/guide/regexp.html

VB 스크립트 혹은 VB에서 정규표현식을 사용하는 방법은 아래 링크에..
http://www.egocube.pe.kr/asp_0003.asp

오죽하면 이런 프로그램들까지 다 나왔습니다..
http://www.vbcity.com/pubs/article.asp?alias=regexp

웹개발자들 사이에서 혹은 VB 개발자들 사이에서 정규표현식이 얼마나 많이 사용되고 있는지 잘보여주고 있습니다..



.NET에서는 아래와 같은 클래스가 제공되므로 이제 .NET을 지원하는 모든 프로그래밍 언어에서 이 클래스들을 사용할 수 있게 되었습니다..
System.Text.RegularExpressions
System.Web.UI.WebControls.RegularExpressionValidator
점점 코딩하기 편해지는 세상인거 같습니다..



우왕.. 절라 편해보이는데.. C++에서는 어케 사용을 해야할까요.. 안타깝게도 MFC나 C++ 표준 라이브러리에는 정규표현식을 지원하는게 없습니다..
음.. 절라 억울합니다.. 하지만, 이 좋은 것을 알고서는 가만 앉아 있을 수만은 없습니다.. 그럼요.. 전세계에 이런 좋은 것을 알고 가만 있지 못하는 C++ 프로그래머가 최소한 몇명은 있을 것이고.. 최소 그중 한명은 웹에 자신의 클래스를 공개했을 겁니다.. 찾아봅시다..
열심히 코드그루나 코드프로젝트 사이트를 찾아보니, 이게 가장 괜찮은거 같더군요.. 하지만 아래 설명할 boost 라이브러리 가지고 삽질 하느라 테스트는 못해봤습니다.. 시간 나시는 분 함 점검 해봐주시길.. 괜찮은지 어떤지..

http://www.codeproject.com/cpp/rexsearch.asp?target=regular%7Cexpression


나머지는 그냥 참고해볼만 합니다..
http://codeguru.earthweb.com/string/regexp.shtml
http://codeguru.earthweb.com/string/reg_ex.shtml
http://www.codeproject.com/string/cperlstring.asp?target=regular%7Cexpression

두둥.. 언제나 진짜는 가장 마지막에 등장하는 법..
여기 아주 막강한 boost 라이브러리의 RegEx++ 이라는 클래스가 있습니다..
boost 라는 라이브러리는 C++ 표준 라이브러리에 자신의 라이브러리를 포함시키길 희망했으나 결국 표준에 채택되지 못하자 이에 불만을 품은 프로그래머들이 차기 표준에 채택될 것을 원하며 따로 모여 만들고 있는 라이브러리인데.. 그중 RegEx++ 이라는 클래스가 정규표현식을 지원한다기에 좀 테스트를 해봤습니다..
사용해본 결과.. 음.. 정말 막강합니다.. 검색에, 치환, 유닉스의 막강한 grep 기능까지.. (사실 grep이 막강한건 정규표현식을 지원하기 때문이죠..) 정규표현식을 사용한 웬만한 문자열 조작은 다 가능하겠더군요.. 게다 많은 사람들이 개발하고 테스트에 참여해 검증도 어느정도 된 상태구요.. 성능도 높이기 위해 많은 노력을 했다고 합니다..

사용법은 다음 링크를 참조하시고.. http://www.codeproject.com/string/Regex__.asp
적용된 예는 맨 아래 소스코드를 참조하세요.. 정말 사용하기 간편하더군요.. 세팅하는게 좀 귀찮아서 그렇지.. (사실 크게 귀찮은 것도 아닙니다..) 그리고 VC++ 6에서 아주 잘 컴파일되고 동작합니다.. (흐흐.. VC++ 6이 워낙에 C++ 표준을 제대로 지원을 못한다는 악명이 높아서리.. 헐헐헐..) 다만.. 좀 아픔이 있습니다.. 무려 500K 짜리 DLL을 같이 배포를 해야 하네요..



그렇다면 저 암호와 같은 정규표현식을 어떻게 만드는 것이냐..
좋은 책이 있습니다.. 열심히 배워야죠.. 정규 표현식 완전 해부와 실습(개정판)

하지만, 또 다른 방법이 있습니다.. 인터넷이 왜 좋겠습니까.. 무수한 사람들이 자신이 삽질해 만들어놓은 정규표현식을 고맙게도 이렇게 한데 다 모아두고 있습니다.. 그냥 찾아서 사용하면 됩니다.. 국내에서만 의미가 있는 우편번호, 전화번호, 주민등록번호 이런것두 있으면 좋을텐데.. 아래의 소스코드는 모두 이곳에서 가져온 정규표현식으로 테스트를 해봤습니다..
http://www.regexlib.com/Default.aspx

여러분.. 문자열 검증, 검색, 치환, 삭제 등에 정규표현식을 적극 활용합시다..
#include <boost/regex.hpp>

using namespace boost ;

// 중간에 위저드가 만들어주는 코드 왕창 생략..

void CRegExTestDlg::OnButton1()
{
   boost::regex expression("^([a-zA-Z0-9_\\-\\.]+)@"
       "((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\"
       ".)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|["
       "0-9]{1,3})(\\]?)$") ;

   char szTemp[1024] ;
   HWND hWnd = ::GetDlgItem(m_hWnd,IDC_EDIT1) ;
   ::GetWindowText(hWnd,szTemp,1024) ;

   CString strTemp ;
   boost::cmatch what ;
   if(boost::regex_match(szTemp, what, expression))
   {
       strTemp.Format("%s는 정상적인 "
           "이메일 주소 입니다.",szTemp) ;
       AfxMessageBox(strTemp) ;

       return ;
   }

   strTemp.Format("이메일 주소 잘못 넣었자녀.. "
       "니 이멜주소를 몰라? 잘 입력해봐~",szTemp) ;
   AfxMessageBox(strTemp) ;
}

void CRegExTestDlg::OnButton2()
{
   boost::regex expression("^[-+]?\\d*\\.?\\d*$") ;

   char szTemp[1024] ;
   HWND hWnd = ::GetDlgItem(m_hWnd,IDC_EDIT2) ;
   ::GetWindowText(hWnd,szTemp,1024) ;

   CString strTemp ;
   boost::cmatch what ;
   if(boost::regex_match(szTemp, what, expression))
   {
       strTemp.Format("%s는 정상적인 "
           "숫자 입니다.",szTemp) ;
       AfxMessageBox(strTemp) ;
       
       return ;
   }

   strTemp.Format("아.. 씨바.. "
       "숫자만 넣으라니까~ !!",szTemp) ;
   AfxMessageBox(strTemp) ;
}

void CRegExTestDlg::OnButton3()
{
   boost::regex expression("^(25[0-5]|2[0-4][0-9]"
       "|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])"
       "\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|"
       "[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-"
       "4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}"
       "|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}"
       "[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$") ;

   char szTemp[1024] ;
   HWND hWnd = ::GetDlgItem(m_hWnd,IDC_EDIT3) ;
   ::GetWindowText(hWnd,szTemp,1024) ;

   CString strTemp ;
   boost::cmatch what ;
   if(boost::regex_match(szTemp, what, expression))
   {
       strTemp.Format("%s는 정상적인 "
           "IP 주소 입니다.",szTemp) ;
       AfxMessageBox(strTemp) ;

       return ;
   }

   strTemp.Format("너 IP주소가 뭔지 모르지 ? "
       "알면 똑바로 넣어봐~ !!",szTemp) ;
   AfxMessageBox(strTemp) ;
}
아주 간단한 사용예입니다.. Boost RegEx의 막강한 문자열 기능들을 보고 싶으시면 여기를 참조하세요.. 이 글의 맨 처음 예제와 같은 비베스크립트 함수와 같은 기능을 쉽게 만들어보고 싶지 않으신가여 ?

단 몇줄로 이루어지는 문자열 검색과 점검, 정말 멋지지 않습니까 ?