C++ 정규식과 Raw String Literal: 매칭과 사용법
정규식을 사용하는 과정에서 가독성이나 사용 편의성을 높이기 위해 C++11부터 도입된 Raw String Literal이 유용하게 활용될 수 있습니다. 이번 포스팅에서는 C++에서 정규식을 활용하는 방법과 함께, Raw String Literal을 사용하는 이유에 대해 설명하고, 구체적인 예시를 통해 그 차이를 알아보겠습니다.
1. 정규식과 코드 예시
다음은 /home/user/test_script.sh 파일 경로가 포함된 문자열을 정규식으로 추출하는 예시입니다. 우선, 정규식을 사용하지 않고 문자열을 단순 비교하는 방법과, 정규식을 사용하는 방법에 대해 살펴보겠습니다.
#include <regex>
#include <iostream>
#include <string>
int main() {
std::string input = R"(RUN+="/home/user/test_script.sh")";
std::regex run_regex(R"(RUN\+=\s*\"([^\"]+)\")");
std::smatch match;
if (std::regex_search(input, match, run_regex)) {
std::cout << "Matched Path: " << match[1] << std::endl;
}
return 0;
}
위 코드에서는 다음과 같은 부분을 중점적으로 살펴봐야 합니다:
- 정규식:
std::regex run_regex(R"(RUN\+=\s*\"([^\"]+)\")"); - 이 정규식은
RUN+="으로 시작하고, 이어서 따옴표(") 안에 있는 파일 경로를 추출합니다. RUN\+=:RUN+문자열과=기호가 나타납니다.\s*: 그 뒤에 올 수 있는 공백을 0개 이상 허용합니다.\"([^\"]+)\": 따옴표(")로 감싸진 문자열을 추출하며, 내부의 파일 경로는 캡처 그룹([^\"]+)로 추출됩니다.
2. Raw String Literal의 사용 이유
정규식 패턴을 다룰 때, C++의 일반적인 문자열 리터럴에서는 이스케이프 문자를 많이 사용해야 합니다. 예를 들어, 백슬래시(\)는 이스케이프 문자이기 때문에, 일반 문자열 리터럴에서는 다음과 같이 작성해야 합니다:
std::regex run_regex("RUN\\+=\\s*\"([^\"]+)\");
위 코드에서는 \ 문자를 두 번 써서 이스케이프 처리를 해야 하므로 가독성이 떨어집니다. 특히 복잡한 정규식을 사용할 때는 이러한 이스케이프 처리가 많아지며, 코드가 더 읽기 어려워집니다.
하지만, Raw String Literal을 사용하면 이러한 문제를 해결할 수 있습니다. R"(... )" 구문 안에서는 이스케이프 문자를 별도로 처리하지 않기 때문에, \도 있는 그대로 사용할 수 있습니다.
std::regex run_regex(R"(RUN\+=\s*\"([^\"]+)\")");
Raw String Literal을 사용하면 백슬래시를 이스케이프 할 필요가 없으므로 코드가 훨씬 간결해지고, 읽기 쉬워집니다.
3. 정규식 설명
위 코드에서 사용된 정규식의 각 요소를 좀 더 자세히 설명하겠습니다.
RUN+=: 이 부분은RUN+이라는 문자열과=기호가 반드시 나타나야 함을 의미합니다. 정규식에서+는 "하나 이상"을 의미하지만, 여기서는 실제로+문자가 패턴의 일부이므로 그대로 매칭됩니다.\s*: 공백 문자(스페이스, 탭 등)를 0개 이상 허용합니다. 즉,=기호 뒤에 공백이 있거나 없을 때 모두 일치합니다.\": 따옴표(")를 찾습니다. C++의 일반 문자열에서 따옴표를 표시할 때는 이스케이프 문자(\)가 필요하기 때문에\"로 표현됩니다. Raw String Literal에서는 그대로" "을 사용할 수 있습니다.([^\"]+): 이 부분은 캡처 그룹입니다.[^\"]+는 "따옴표가 아닌 문자"를 의미하며, 하나 이상의 문자를 매칭합니다. 즉, 따옴표로 둘러싸인 문자열 안의 내용을 추출합니다.- 마지막
\": 문자열이 닫히는 따옴표입니다.
4. 자주 사용하는 정규식 기호
정규식에서는 다양한 기호를 통해 복잡한 문자열 패턴을 처리할 수 있습니다. 아래는 정규식에서 자주 사용하는 기호들과 그 의미를 표로 정리한 것입니다.
| 정규식 기호 | 설명 | 예시 |
|---|---|---|
. |
임의의 한 문자(개행 문자 제외) | a.b는 a와 b 사이의 한 문자와 일치 |
^ |
문자열의 시작을 의미 | ^abc는 "abc"로 시작하는 문자열과 일치 |
$ |
문자열의 끝을 의미 | abc$는 "abc"로 끝나는 문자열과 일치 |
* |
앞의 문자가 0회 이상 반복 | a*는 "a"가 0회 이상 나타나는 문자열과 일치 |
+ |
앞의 문자가 1회 이상 반복 | a+는 "a"가 1회 이상 나타나는 문자열과 일치 |
? |
앞의 문자가 0회 또는 1회 나타남 | a?는 "a"가 없거나 1번 나타나는 문자열과 일치 |
\ |
이스케이프 문자로, 특수 문자를 문자 그대로 사용 | \\는 백슬래시 자체와 일치, \.는 마침표와 일치 |
| |
OR 연산자로 둘 중 하나와 일치 | a|b는 "a" 또는 "b"와 일치 |
[] |
문자의 집합(배열)을 정의 | [abc]는 "a", "b", 또는 "c"와 일치 |
[^] |
대괄호 안에 있는 문자를 제외한 모든 문자와 일치 | [^abc]는 "a", "b", "c"를 제외한 모든 문자와 일치 |
() |
그룹을 지정하고 캡처함 | (abc)는 "abc"와 일치하며, 캡처 그룹으로 저장 |
{n} |
정확히 n번 반복 | a{3}는 "aaa"와 일치 |
{n,} |
최소 n번 반복 | a{2,}는 "aa", "aaa", "aaaa" 등과 일치 |
{n,m} |
최소 n번, 최대 m번 반복 | a{2,4}는 "aa", "aaa", "aaaa"와 일치 |
\d |
숫자와 일치 (0-9) | \d는 숫자와 일치 |
\D |
숫자가 아닌 문자와 일치 | \D는 숫자가 아닌 문자와 일치 |
\w |
단어 문자를 의미 (알파벳, 숫자, 언더바) | \w는 "a-z", "A-Z", "0-9", "_"와 일치 |
\W |
단어 문자가 아닌 문자와 일치 | \W는 단어 문자가 아닌 문자와 일치 |
\s |
공백 문자(스페이스, 탭, 개행 등)와 일치 | \s는 공백 문자와 일치 |
\S |
공백 문자가 아닌 문자와 일치 | \S는 공백 문자가 아닌 문자와 일치 |
(?=...) |
전방 탐색(뒤에 오는 조건이 맞는지 확인, 패턴에 포함하지 않음) | a(?=b)는 "ab"에서 "a"만 일치 |
(?!...) |
부정 전방 탐색(뒤에 오는 조건이 없는 경우 일치) | a(?!b)는 "ab"가 아닌 "a"와 일치 |
마무리
이번 포스팅에서는 C++에서 정규식을 사용하는 방법과 함께, Raw String Literal의 유용성에 대해 알아보았습니다. 특히, 정규식에서 백슬래시(\)와 같은 특수 문자를 처리하는 과정에서 Raw String Literal이 코드의 가독성을 크게 높일 수 있음을 확인할 수 있었습니다.
정리된 예시는 정규식 패턴을 이용하여 파일 경로를 쉽게 추출할 수 있는 좋은 방법을 제공합니다. Raw String Literal을 사용하면, 코드 작성과 유지보수가 더 쉬워지므로, 정규식을 자주 사용하는 개발자라면 적극 활용해 보시기 바랍니다!
'MFC(Window Programming)' 카테고리의 다른 글
| Mutex, Semaphore, Critical Section 동기화 메커니즘 성능 비교 (0) | 2024.09.27 |
|---|---|
| ifstream, ostream, stringstream, std::getline (1) | 2024.09.25 |
| OnSize 함수 호출 방법 MFC (0) | 2021.01.07 |
| timespan, ctime 사용법 C/C++ MFC (0) | 2021.01.05 |
| % 기호 출력하기 printf C/C++ (0) | 2021.01.05 |