[C++] 정규 표현식

C++11부터 정규 표현식이 표준 라이브러리에 포함됐다. 문자열 다루기 힘든 C++에서 이런게 되다니 정말 기가 찰 노릇이다. 구슬이 서말이라도 꿰어야 보배이듯이 표준 라이브러리에 있어도 사용법을 모른다면 의미가 없다. 간략하게 사례별로 어떻게 사용하는지 살펴보도록 하자. 일단 코드를 먼저 보고,…​

#include <stdio.h>
#include <string>
#include <regex>

int main()
{
	std::string s = "123";
	std::regex number("[0-9]+");

	if(std::regex_match(s, number))
	{
		printf("number\n");
	}
	else
	{
		printf("not number\n");
	}

	return 0;
}

정규 표현식을 이용해서 문자열이 숫자인지 아닌지를 판단하는 프로그램을 만들어 본 것이다. s의 값을 변경해가면서 출력 값을 체크해보면 대략 어떻게 쓰는 것인지 알 수 있다. std::regex_match 함수를 사용하면 특정 문자열이 정규 표현식에 부합하는지 아닌지를 체크할 수 있다는 사실을 알면 되겠다. 한 가지 주의해야 할 점은 std::regex_match 함수의 경우 재귀 호출을 사용한다는 점이다. 따라서 정규 표현식에 따라서 문자열이 길면 스택 오버플로로 크래시가 발생할 수 있다. Visual Studio 2015에서는 이러한 문제점을 없애기 위해서 재귀 호출의 최대 깊이를 상수로 지정할 수 있도록 되어 있다. regex를 포함하기 전 _REGEX_MAX_STACK_COUNT를 특정 값으로 선언해 주면 된다. 기본적으로 64비트는 600, 32비트는 1000으로 설정돼 있다. 그 크기를 늘려주면 더 긴 문자열도 오류 없이 매치할 수 있다. 물론 너무 높게 설정하면 스택 오버플로로 크래시가 발생할 수 있다.

#include <stdio.h>
#include <string>
#include <regex>

int main()
{
	std::string s = "123 456 789";
	std::regex number("[0-9]+");

	std::sregex_iterator it(s.begin(), s.end(), number);
	std::sregex_iterator end;

	while(it != end)
	{
		std::smatch m = *it;

		printf("%s\n", m.str(0).c_str());

		++it;
	}

	return 0;
}

위 예제는 특정 문자열에서 정규 표현식에 부합하는 요소만 추출해서 출력하는 예를 보여주고 있다. std::sregex_iterator를 사용해서 부합하는 요소만 열거할 수 있고, std::smatch 객체를 이용하면 부합한 내용이 무엇인지 출력할 수 있다. 위 프로그램의 출력 결과는 123, 456, 789가 된다. std::smatch의 str 함수의 첫번째 파라미터로 넘어간 숫자가 애매한데 그건 매치한 대상 중에 어떤 것을 출력할지를 결정한다. 위 예에서는 매치 대상이 별도로 구분되어 있지 않기 때문에 0번 밖에는 없다. 아래 예제를 살펴보자.

#include <stdio.h>
#include <string>
#include <regex>

int main()
{
	std::string s = "123 456 789";
	std::regex number("([0-9])([0-9])([0-9])");

	std::sregex_iterator it(s.begin(), s.end(), number);
	std::sregex_iterator end;

	while(it != end)
	{
		std::smatch m = *it;

		printf("%s\n", m.str(1).c_str());

		++it;
	}

	return 0;
}

위 프로그램의 출력 결과는 1, 4, 7이 된다. 정규 표현식이 ([0-9])([0-9])([0-9]) 이와 같이 구성되어 있기 때문에 123이 이 표현식에 매치되면 std::smatch의 str 함수의 0번은 123을, 1번은 1을 2번은 2를, 3번은 3을 나타낸다. 그렇다면 정규 표현식 전체를 괄호로 한 번 더 묶으면 어떻게 될까? "(([0-9])([0-9])([0-9]))" 이렇게 말이다. 이러면 0은 123을 1도 123을 2는 1을, 3은 2를, 4는 3을 반환한다.