::NPTEAM:: Network Programer Team

검색 :
RSS 구독 : 글 / 댓글 / 트랙백 / 글+트랙백

정규 표현식 컴파일 문제

비주얼 스튜디오 2008 SP1에서 정규표현식을 사용하기 위한 방법은 2가지가 있다.
 1. std::tr1::regex, std::tr1::wregex 를 활용한 방법
 2. boost의 regex를 사용하는 방법

우선 여기에선 정규표현식을 사용하기 전에 정규표현식 자체의 문법이 올바르지 않을때 발생하는
문제점에 대해서 알아보기로 한다.
#include < iostream >
#include < regex >

int _tmain(int argc, _TCHAR* argv[])
{
	setlocale( LC_ALL, "Korean" );

	std::wstring strBadWord( _T("이런 바보 같은 놈들") );

	std::tr1::wregex regularExpression( _T("바보") );

	std::wstring strResult;
	strResult = std::tr1::regex_replace( strBadWord, regularExpression, std::wstring( _T("") ) );

	std::wcout << strResult << std::endl;

	return 0;
}
위 예제의 결과 "바보"가 "" 빈 문자열로 대체되고, "이런 같은 놈들" 문자열만 남게 된다.
여기에서 "바보" 대신에 정규표현식 문자열을 대체하여 사용 가능하다.
정규표현식에 대한 자세한 정보는 검색엔진에서 더욱 훌륭한 설명을 얻을 수 있기 때문에,
실제로 잘못된 정규표현식을 넣으면 어떻게 되는지 살펴보기로 한다.
std::tr1::wregex regularExpression( _T("[바|보]") );
위의 표현식은 "바" 또는 "보" 글자 중 하나가 있는지 살펴보는 정규표현식이다. 위 표현식에서 "[" 로 열고 "]"를 닫지 않은 표현식을 사용하면 런타임 에러가 발생한다.
std::tr1::wregex regularExpression( _T("[바|보") );
위와 같이 런타임 에러가 발생하는 이유는 정규표현식 문자열을 내부적으로 컴파일할때 표현식이 올바르지 않으면 Throw 예외를 발생시키기 때문이다. 이를 막기 위한 코드는 아래와 같다.
 
#include < iostream >
#include < regex >

int _tmain(int argc, _TCHAR* argv[])
{
	setlocale( LC_ALL, "Korean" );

	std::wstring strBadWord( _T("이런 바보 같은 놈들") );

	std::tr1::wregex regularExpression;
	try
	{
		regularExpression.assign( _T("[바|보") );
	}
	catch (std::tr1::regex_error e )
	{
		std::wcout << e.what() << std::endl;
		return 0;
	}

	std::wstring strResult;
	strResult = std::tr1::regex_replace( strBadWord, regularExpression, std::wstring( _T("") ) );

	std::wcout << strResult << std::endl;

	return 0;
}
이처럼 try, catch문을 사용하여 정규표현식 에러에 대한 예외처리를 해야만 한다.

try, catch문 사용이 부담되는 프로그래머 입장에서 볼 때,
정규표현식이 올바른지 검사하는 API가 외부에 존재한다면 얼마나 좋을까 하는 아쉬움이 남는다.
2010/05/09 19:53 2010/05/09 19:53

맨 위로

정규식을 이용하여 보다 쉽게 쿼리문을 작성해 보자_02

DB 쿼리 작업을 하다보면, 테이블에 insert 쿼리문를 대량으로 작성해야 하는 경우가 많다.
특히, 엑셀로 작업한 결과물을 DB에 insert 하거나, update 해야 할 경우 쿼리문 작성으로 고단한 경우가 종종 발생한다.

정규식을 활용하여 원하는 정보를 쿼리식으로 손쉽게 만드는 방법에 대해서 알아보자.


위와 같은 테이블 정보에 아래의 데이터를 INSERT 해야 한다.

엑셀과 같이 입력하고 싶은 자료를 정규식을 지원하는 Notepad2와 같은 편집기에 복사 & 붙여넣기한다.

라인의 처음 시작 부분은 정규식에서 ^로 표시한다.

^(\d{4})한 줄의 시작 부분이 숫자(\d)로된 4자리 글자를 먼저 선택한다.(숫자 4자리 선택)
이렇게 한 줄의 첫번째 되는 부분을 정확히 일치시켜야 줄 중간에서 숫자 4자리를 무시하고 처리할 수 있다.
선택 범위 :  [8001]

^(\d{4})[\t]+숫자 4자리로 시작하고, 그 다음 탭으로 떨어진 공간을 선택한다.(TAB까지 선택)
선택 범위 :  [8001 TAB]

^(\d{4})[\t]+([^\t]+)탭으로 떨어진 공간 다음에, 탭이 아닌 문자를 선택한다.(TOM까지 선택)
선택 범위 :  [8001 TAB TOM]

위와 같이 탭으로 떨어진 문자 [\t]+, 탭이 아닌 문자 ([^\t]+) 를 반복적으로 기입한다.

^(\d{4})[\t]+([^\t]+)[\t]+([^\t]+)[\t]+([^\t]+)[\t]+([^\t]+)[\t]+([^\t]+)[\t]+([^\t]+)[\t]+(\d+)$선택 범위 :  [8001 TAB TOM TAB DBA TAB 8400 TAB 1981-12-03 TAB 3000 TAB 10 TAB 40]

중간 부분은 [\t]+ 탭과 ([^\t]+) 탭을 포함하지 않은 문자열로 구분하면 되지만,
시작부분과 마찬가지로 종료 부분에는(\d+)$      : 숫자로 종료(0-9)
(\w+)$      : 문자로 종료(a-z, A-Z)
([\d|\w]+)$ : 숫자나 문자로 종료(0-9, a-z, A-Z)
를 명시적으로 표기해야 한다.
(단, \w는 a-z, A-Z의 문자열을 말하며 한글 문자열을 지원하지 않는다.)

위와 같이 작성된 정규표현식을 찾을문자에 입력하고 찾기 버튼을 누르면, 한 줄씩 선택되는 것을 확인할 수 있다.

이때, 중요한 점! 지난번에도 언급했듯이 정규표현식에서 () 괄호로 묶은 것은 그룹으로 처리된다.
첫번째 괄호로 묶인 문자열은 \1로 치환할 수 있고,
두번재 괄호로 묶인 문자열은 \2로 치환할 수 있다....
또한, 괄호로 묶인 전체 문자열 \0로 치환할 수 있다.

우리가 치환하고자 하는 부분은 이미 괄호로 8번 묶어 놓은 상태 이므로,
바꿀 문자에 다음과 같이 입력하고 모두 바꾸기를 수행해 보자.

INSERT INTO TEST_EMP (EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) values ( \1, '\2', '\3', \4, '\5', \6, \7, \8);


위와 같이 모두 바꾸기를 수행하면 다음과 같은 결과를 볼 수 있다.

지금까지 INSERT 문을 정규표현식을 이용하여 간단히 바꾸는 과정을 알아 보았다.

간단하지만 강력한 정규표현식.
위와 같이 적은 양의 자료를 입력할때에는 복사 붙여넣기로 해결되지만,
최소 천건에서 2-3만건의 자료를 처리할 때에는 정규표현식을 만드신 분이 정말 눈물나게 고맙게 느껴진다. ^^;

2009/03/28 11:32 2009/03/28 11:32

맨 위로

정규식을 이용하여 윈도우 방화벽 사용자 지정 목록 편집하기


윈도우 방화벽은 이제 없어서는 안될 필수품이 되었다.
그렇지만 원하는  IP 주소 범위를 변경하기 위해서는 엄청난 키보드 화살표 오른쪽 버튼의 압박에 시달리게 된다.

빌 게이츠 횽 왜 윈도우 방화벽은 이렇게 사용하기 불편한 건가요?


이렇게 탄식을 하더라도, MS에서 방화벽 UI를 바꿔주길 기대하는 것은 무리다.
"윈도우 그림판으로 포토샵처럼 편집하고 싶어요~" -_-;

우리는 정규표현식을 이용해서 이번 난관도 아주 약간 편하게 헤쳐나갈 수 있다.


사용자 지정 목록의 내용을 복사해서 notepad2에 붙여넣기 해 보자.
(안타깝게도 MS 기본 메모장에서는 정규식을 지원하지 않는다.)


오~ 스크롤 압박!!
모니터를 확! ... 참자, 우리에겐 정규표현식이 있다.



찾을 문자에 ,
바꿀 문자에 ,\r\n
을 입력하고 모두 바꾸기를 클릭한다.



오~ 완전 깔끔하게 정리되었다.
IP 범위 수정을 모두 마쳤으면, 바꿀 문자와 찾을 문자를 바꿔서 넣고 모두 바꾸기를 한다.



다시 1줄로 정리되었다.

이렇게 쉽고 강력하지만, 우리는 윈도우 메모장에서는 정규표현식을 사용할 수 없다.

빌 게이츠 횽 UI 개선 안해줘도 좋은데요. 메모장에서 정규표현식 지원해 주시면 안되나요?
2009/03/21 04:30 2009/03/21 04:30

맨 위로

정규식을 이용하여 보다 쉽게 쿼리문을 작성해 보자_01

위와 같이 test_emp 테이블에서 원하는 EMPNO를 선택하여,

select *
from test_emp
where in ( ... )

select 쿼리문을 만들다 보면, EMPNO 컬럼을 드래그 복사해서 where in ( 7369, 7499 ...)
이렇게 한번에 넣고 싶을때가 많다.

이렇게 선택하고 텍스트 편집기 창에 복사하면, 다음과 같이 나온다.

위와 같이 나온 text를 전체 선택하여, 주석처리를 한다.

그리고 다음과 같이 "--" 텍스트를 ", "로 치환하고

다음과 같이 쿼리문을 작성하여 실행하면, 편하게 쿼리문을 작성할 수 있다.


이건 누구나 다 아는 내용이다.
게다가 정규식은 보이지도 않는데 어떻게 된것이냐?
 라고 생각하시는 분들을 위해서 이제부터 본격적으로 정규식을 써 보겠습니다.



위와 같이 선택된 값을 다음과 같은 쿼리문으로 만들려고 합니다.
select *
from test_emp
where
OR ( JOB = 'CLERK' AND EMPNO = '7369' )
OR ( JOB = 'CLERK' AND EMPNO = '7900' )
OR ( JOB = 'MANAGER' AND EMPNO = '7698' )
OR ( JOB = 'SALESMAN' AND EMPNO = '7654' )
이렇게 쿼리를 만들고 where 다음의 OR 글자만 지워주면 됩니다.
아래의 정규표현식을 실행해볼까요?

와우! 정말 한번에 원하는 쿼리와 근접하게 변환되었습니다.


여기까지 잘 따라오셨나요?
이쯤에서 위에서 쓰인 정규표현식을 설명하고 넘어가기로 하죠.

위에서 빨간색 네모로 표시된 정규식은 다음과 같습니다.
^(\w+)\t(\d+)^  : 한 줄의 시작부분을 의미합니다.
     ^이 없으면, 한 줄의 중간 부분에서도 정규식 패턴이 발견되면, 해당 문자열을 찾아냅니다.
() : 괄호로 묶은 곳이 2군데가 있는데요.
     괄호로 문장을 묶으면 해당 문자열을 그룹으로 치환할 수 있습니다.
     이 부분은 아래에서 더 자세히 설명드리죠.
\w : 알파벳으로 이루어진 한 문자를 찾습니다. (a~z, A~Z) 사이의 문자를 말하는거죠.
\t : 탭으로 띄어쓰기된 공간을 찾습니다.
\d : 숫자로 이루어진 한 문자를 찾습니다. (0~9) 사이의 문자를 찾습니다.
+  : 바로 앞의 문자가 1번이상 계속 반복되는 것을 말합니다.


해석하면,
1. 한 줄의 시작부분이 (\w+) 알파벳으로 연속된 문자열을 찾아서 그룹으로 묶는다.
2. \t 탭으로 띄어쓰기된 공간을 찾는다.
3. (\d+) 숫자로 연속된 문자열을 찾아서 그룹으로 묶는다.


여기까지 이해 가셨나요? 그렇다면 바꿀 문자(파란 네모안의 정규식)에 해당하는 정규식을 살펴 보기로 하죠.
OR ( JOB = '\1' AND EMPNO = '\2' )여기에서는 중요한 포인트가 바로 \1 과 \2 입니다.
\1 : 찾을 문자(빨간 네모안의 정규식)에서 첫번째로 그룹화된 문장을 이 부분에 끼워넣기 한다.
\2 : 찾을 문자(빨간 네모안의 정규식)에서 두번째로 그룹화된 문장을 이 부분에 끼워넣기 한다.

자 이제 이해가 가셨나요?
정규표현식으로 다양한 문자 치환 방법이 있는데요. 그중에서 자주 쓰이는 그룹 치환에 대해서 알아 보았습니다.

이처럼 정규표현식을 활용하면, 다양하고 복잡한 문자열을 손쉽게 바꿀 수 있습니다.

TIP.
Tab으로 띄어진 문자열을 구분하기 위해서는 다음의 정규식으로 검색한 후 치환하세요.
^(\d+|\w+)\t(\d+|\w+)\t(\d+|\w+)
2009/03/21 03:26 2009/03/21 03:26

맨 위로

정규식을 이용해서 ""를 _T("") 형식으로 바꾸자!



정규식을 이용하면 문자열 처리를 보다 다양하고 간편하게 처리할 수 있다는 것을 알아 보았다.

ANSI 코드 문자열을 Unicode 문자열로 변환하기 위해서는 "xxx" -> _T("xxx") 로 치환해 주어야 한다.
전처리기 _T(x) 는 다음과 같이 정의되어 있다.
 
#define _T(x)       __T(x)
#define _TEXT(x)    __T(x)
 
#ifdef  _UNICODE
  #define __T(x)      L ## x
#else
  #define __T(x)      x
#endif

_UNICODE       이면 L"xxx"로 치환,
_UNICODE가 아니면   "xxx"로 치환.

그렇다면 단순히 "xxx"를 찾아서 _T("xxx")로 치환해 주면 간단히 유니코드를 지원하는 문자열이 된다. ("말이 쉽지 어느 세월에 다 바꾸냐?" 하고 생각하시는 분들은 아랫글을 봐 주세요. ^^; )

그래서 짜잔!! 정규식의 활용 2번째. 말이 길어지면 지루해지는 법. 결과부터 볼까요?


왼쪽의 소스코드에는 "xxx" 와 같은 문구가 4번 등장한다.
  • #include "stdafx.h" 보라색 밑줄
  • main() 안의 초록색 밑줄

그리고 오른쪽의 바꾸기 탭에 "찾을 내용""바꿀 내용" 그리고 찾기 옵션의 정규식 부분을 잘 보자!

찾을 내용 : (#으로 문장이 시작되고, "를 만날때까지)를 무시하고, ("로 시작해서 "가 아닌 문자열들을 포함하고 "로 끝나는 부분)을 찾는다.

~(^\#[^"]*)("[^"]*")

바꿀 내용 : \0 위에서 파란색 괄호에 둘러쌓인 부분을 선택하고, 앞에는 _T(를 붙이고 뒤에는 )를 붙인다.
_T(\0)

이렇게 전체 바꾸기를 수행한 다음 결과를 보도록 하자.

위와 같이 #으로 시작하는 "xxx" 부분(보라색 밑줄)은 바뀌지 않았고,
#으로 시작하지 않는 "xxx" 부분(초록색 밑줄)이 _T("xxx")로 치환 되었다.

MSDN 참조 : http://msdn.microsoft.com/ko-kr/library/aa293063(VS.71).aspx
                  http://msdn.microsoft.com/ko-kr/library/2k3te2cs(VS.80).aspx

2008/12/15 20:03 2008/12/15 20:03

맨 위로

정규식을 써보자!


정규식을 설명하기 위해서 한가지 예를 들기로 한다.(http://www.npteam.net)

^ht{2}p:(/{2})w{3}(.)(n.{0,10})(?:com|net|(co.kr))
  1. 문장의 처음은 h로 시작한다.
  2. "t"글자가 연속 2번 출력된다.
  3. "p:" 가 출력된다.
  4. "/"가 연속 2번 출력된다.
  5. "w"가 연속 3번 출력된다.
  6. 아무글자 1글자를 그대로 출력한다.
  7. n으로 시작해서 최소 0에서 최대 10글자까지 아무글자나 출력한다.
  8. 맨 뒤에 종결 문자는 "com", "net", "co.kr" 3가지중 하나라도 만족하면 출력한다.
윈도우에서 모든것을 표현하는 *문자는 ".*"로 사용가능하다.

따라서 위의 정규식으로 출력되는 결과를 찾으면 다음과 같이 출력된다.


이 수식을 이용해서 VBScript에서 이용하려면 다음의 샘플 코드를 사용하시면 됩니다.

■ 정규식에 의해서 일치하는 문장이 있는지 없는지 판단하는 스크립트
Dim objRegExp
Set objRegExp = New RegExp
objRegExp.MultiLine = True
objRegExp.Pattern = "^ht{2}p:(/{2})w{3}(.)(n.{0,10})(?:com|net|(co.kr))"
 
SubjectString = "http://www.npteam.net"
 
If objRegExp.Test(SubjectString) Then
  ' Successful match
  MsgBox "Success"
Else
  ' Match attempt failed
  MsgBox "Failed"
End If


■ 정규식에 의해서 일치하는 문장을 리턴받고 싶을 경우
Dim ObjRegExp, MatchWords, ResultString
Set ObjRegExp = New RegExp
ObjRegExp.MultiLine = True
ObjRegExp.Pattern = "^ht{2}p:(/{2})w{3}(.)(n.{0,10})(?:com|net|(co.kr))"
 
SubjectString = "http://www.npteam.net"
 
Set MatchWords = ObjRegExp.Execute(SubjectString)
If MatchWords.Count >= 1 Then
  ResultString = MatchWords(0).Value
Else
  ResultString = "Nothing"
End If
 
MsgBox ResultString


이해를 돕기 위해 샘플 정규식을 한가지 소개하고자 한다.

■ 숫자 000 ~ 255
^([01][0-9][0-9]|2[0-4][0-9]|25[0-5])$
  1. 첫번째 자리의 숫자 (0, 1), 두번째 자리 숫자(0~9), 세번째 자리 숫자(0~9)
  2. 첫번째 자리의 숫자(2), 두번째 자리 숫자(0~4), 세번째 자리 숫자(0~9)
  3. 첫번째 자리의 숫자(2), 두번째 자리의 숫자(5), 세번째 자리의 숫자(0~5)

결국 각각 다음과 같은 범위를 제한하고 있다.

000 ~ 199, 200 ~ 249, 250 ~ 255

왜 이렇게 3단계로 분류해야 할까?
정규식은 숫자를 정수로 판단하는 것이 아니라 각각 하나의 문자로 판단하기 때문이다.
따라서 문자가 올 수 있는 범위를 세부적으로 제한함으로써 위와 같은 결과를 얻을 수 있다.

이외에도 정규식을 활용하면 수 많은 표현을 아주 간결하게 찾아낼 수 있다.

 
2008/08/05 02:07 2008/08/05 02:07

맨 위로