Advanced C++

[C++] 정규식 표현 std::regex으로 문자열 찾기

로파이 2022. 2. 7. 22:16

C++11에 포함된 <regex> 라이브러리에 정규식 표현과 관련된 기능이 있다.

 

std::regex

정규식 문자열을 받아 정규식을 표현하는 개체

std::regex rx("[\\w]+"); // 단어 단위로 하나이상 매칭시키는 정규식

 

C++/C# 정규식 표현 치팅 시트

https://download.microsoft.com/download/D/2/4/D240EBF6-A9BA-4E4F-A63F-AEB6DA0B921C/Regular%20expressions%20quick%20reference.pdf

https://docs.microsoft.com/ko-kr/dotnet/standard/base-types/regular-expression-language-quick-reference

 

자주 사용되는 정규식 표현

- [A-Za-z] : 영소대문자 집합 == [\\w]와 같은 의미

- [\\w\\d] : 영소대문자 혹은 숫자로 이루어진 단어

- [a]+ : 문자열 a가 적어도 하나 이상, [a]? a가 하나 있거나 없거나

- [A-Z]{3,4} : 영대문자가 3~4글자 포함되어야함 

- ^[\\d] : 숫자로 시작하는, [\\d]$ 숫자로 끝나는

- a.c : 와일드 카드 (abc, adc, aed), a*e : a로 시작하고 e로 끝나는 모든 문자열

- \w / \d와 같은 경우 이스케이프 문자가 문자열 내에서 캐치되도록 \\(\\w, \\d)를 사용해야한다.

- 그룹화 (정규식 표현1)(정규식 표현2)... 

 

사용 패턴

std::regex_match

1. 전체 문자열을 대상으로 해당 정규식 표현이 매칭되는 지 알고 싶은 경우

bool match = std::regex_match("대상 문자열", rx);

#define REG_MATCH(expression, text)\
std::regex reg_match(expression);\
std::cout << "text: " << text << std::endl;\
std::cout << "regex: " << expression << " match: " << std::boolalpha << std::regex_match(text, reg_match) << std::endl;

static constexpr const char* text3 = "---this is an example----"
static void MatchExample3()
{
	const char* exp = "-+([A-Za-z ]+)-+";

	REG_MATCH(exp, text3); // true
}

 

2. 그룹을 이용한 정규식 표현으로 문자열 캐치하기

std::regex_match와 std::smatch를 이용하여 정규식의 그룹들을 분리해낸다.

std::regex_match가 true인 경우 전체 문자열이 첫번째 그룹이 되고

그 이후 (괄호) 순서에 따라 그룹 인덱싱으로 분리해낼 수 있다.

static void MatchExample6()
{
	const char* exp = "^(\\w+)://([\\w.]+)(:(\\d+))?([\\w./]+)?(\\?([\\w=]*))?(#([\\w]+))?$";

	std::string uri("https://www.naver.com:80/kr/index.html?lite=true#ui");
	std::regex rx(exp);

	auto matches = std::smatch();
	if (std::regex_match(uri, matches, rx))
	{
		if (matches[1].matched)
		{
			std::cout << "protocol: " << matches[1].str() << std::endl;
		}

		if (matches[2].matched)
		{
			std::cout << "domain: " << matches[2].str() << std::endl;
		}

		if (matches[4].matched)
		{
			std::cout << "port: " << matches[4].str() << std::endl;
		}

		if (matches[5].matched)
		{
			std::cout << "path: " << matches[5].str() << std::endl;
		}

		if (matches[7].matched)
		{
			std::cout << "query: " << matches[7].str() << std::endl;
		}

		if (matches[9].matched)
		{
			std::cout << "fragment: " << matches[9].str() << std::endl;
		}
	}
}

std::sregex_iterator

3. 전체 문자열 중 정규식 표현에 매칭되는 부분 문자열을 모두 찾는 경우

static void MatchExample2()
{
	static constexpr const char* example = "(This) {is} <an> [example]";
	const char* exp = "[\\[\\{\\(\\<]([A-Za-z]+)[\\]\\}\\)\\>]";

	std::regex rx(exp);
	std::string sss(example);
	std::cout << "text: " << example << std::endl;
	std::sregex_iterator iter(sss.begin(), sss.end(), rx);
	std::sregex_iterator iterEnd;
	std::cout << "found: ";
	while (iter != iterEnd)
	{
		if (iter->size() > 1)
		{
			std::cout << "(" << (*iter)[1] << ") ";
		}
		++iter;
	}
	std::cout << std::endl;
}

4. 정규식 표현과 일치하는 모든 부분 문자열의 수

static void MatchExample7()
{
	const char* exp = "apple|Apple";
	static constexpr const char* tt = "My Apple device counts apples for apple service as an Apple user like an apple.";

	std::regex rx(exp);
	std::string str(tt);
	std::cout << "text: " << str << std::endl;
	std::sregex_iterator iter(str.cbegin(), str.cend(), rx);
	std::sregex_iterator iterEnd;

	auto count = std::distance(iter, iterEnd);
	std::cout << "expression: " << exp << std::endl;
	std::cout << "count: " << count << std::endl;
}

 

std::regex_replace

5. 정규식 표현으로 문자열 치환하기

std::string dayMonthYearDate("31-10-2020");
const char* exp = "(\\d{1,2})(.|-)(\\d{1,2})(.|-)(\\d{4})";
std::regex rx(exp);

// "2020-10-31"
std::string yearMonthDayDate = std::regex_replace(dayMonthYearDate.data(), rx, "$5-$3-$1");