C++을 사용하다 보면 다른 언어의 라이브러리들이 부러운 경우가 많이 있다.

정규표현식은 그 중 하나였는데, 다행히도 VS2008 SP1 이상을 쓰고 있다면 TR1이 내장되어 있어 다른 어떤 써드파티 라이브러리를 연결하지 않고도 #include <regex> 한 줄만 추가해줌으로써 쉽게 사용할 수 있게 되었다.

다음은 원하는 문자열을 매치해서 결과를 받아보는 간단한 예제이다.

const std::string s("Hello World");
 
std::tr1::smatch m;
std::tr1::regex rx("(\\w+) (\\w+)");
bool fMatched = std::tr1::regex_match( s, m, rx );
 
if ( fMatched )
{
    std::cout << "size : " << m.size() << std::endl <<
                    "match0 : " << m[0] << std::endl << 
                    "match1 : " << m[1] << std::endl << 
                    "match2 : " << m[2] << std::endl;
}

// size : 3
// match0 : Hello World
// match1 : Hello
// match2 : World

match 객체의 첫번째 요소에는 매치된 모든 문자열이 담기게 되고, 그 다음 요소들 부터는 캡쳐한(괄호로 둘러싼) 문자열들이 저장된다.
위 예제에서는 첫번째 단어와 두번째 단어를 캡쳐해봤다. 물론 캡쳐 없는 괄호 (?: )을 사용해서 캡쳐 기능을 제거할 수도 있다.
파이썬이나 C# 등의 다른 정규식 라이브러리에서도 대부분 위와 비슷한 사용법을 제공한다.

문자열 매칭뿐만 아니라 치환도 할 수 있다.

const std::string s("Hello World");
std::tr1::regex rx("^\\w+");
std::string t = std::tr1::regex_replace( s, rx, std::string("Great") );
std::cout << t << std::endl;

// 위 코드는 첫 번째 단어를 "Great" 라는 문자열로 치환시킨다.
// Great World

치환할 때 백레퍼런스도 역시 사용이 가능하다.

std::tr1::regex rx("(^\\w+)");
std::string t = std::tr1::regex_replace( s, rx, std::string("Great $1") );

// 첫번째 캡쳐그룹(괄호 안)을 치환시에 재사용 하였다.
// Great Hello World

간단한 예제이지만 잘 응용하면 많은 곳에 적용할 수 있을 것이다.

예전에 이메일 검증 함수를 골뱅이와 .을 찾아가면서 CString의 Find 함수로 떡칠을 하면서 만든적이 있었는데, 시간이 지나고 어느 날 RFC 문서에서 이메일 형식 명세를 보다가 아 내가 엉터리로 만들었구나 하고 깨달았던 기억이 난다.
Email이나 URL 형식 같은 것들은 정규식 없이 검증 함수를 만들기엔 생각보다 훨씬 복잡하다.

다른 언어들보다 사용법이 조금 까다롭고 보기에도 좋지는 않지만 이 정도만으로도 C++ 프로그래머들은 고마움에 눈물이 날만하다.

C++0x에서는 파이썬의 r""이나 C#의 @"" 같은 raw string 기능도 언어에 포함될 예정인데, 그 때가 되면 조금 더 보기 좋게 정규식을 사용할 수 있을 것이다.

함께 읽으면 좋은 글: