C++ 정규표현식 | "regex" 완벽 가이드

C++ 정규표현식 | "regex" 완벽 가이드

이 글의 핵심

C++ 정규표현식에 대해 정리한 개발 블로그 글입니다. #include <regex> #include <iostream> using namespace std;

기본 사용법

#include <regex>
#include <iostream>
using namespace std;

int main() {
    regex pattern("\\d+");  // 숫자 패턴
    
    string text = "abc123def456";
    
    // 검색
    if (regex_search(text, pattern)) {
        cout << "숫자 발견" << endl;
    }
}
regex pattern("\\d+");

string s1 = "123";
string s2 = "abc123";

// regex_match: 전체 문자열 매칭
cout << regex_match(s1, pattern) << endl;  // 1 (true)
cout << regex_match(s2, pattern) << endl;  // 0 (false)

// regex_search: 부분 문자열 매칭
cout << regex_search(s1, pattern) << endl;  // 1
cout << regex_search(s2, pattern) << endl;  // 1

캡처 그룹

regex pattern("(\\d{3})-(\\d{4})-(\\d{4})");
string phone = "010-1234-5678";

smatch match;
if (regex_match(phone, match, pattern)) {
    cout << "전체: " << match[0] << endl;  // 010-1234-5678
    cout << "지역: " << match[1] << endl;  // 010
    cout << "중간: " << match[2] << endl;  // 1234
    cout << "끝: " << match[3] << endl;    // 5678
}

실전 예시

예시 1: 이메일 검증

bool isValidEmail(const string& email) {
    regex pattern(R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)");
    return regex_match(email, pattern);
}

int main() {
    cout << isValidEmail("[email protected]") << endl;  // 1
    cout << isValidEmail("invalid.email") << endl;     // 0
}

예시 2: URL 파싱

struct URL {
    string protocol;
    string host;
    string port;
    string path;
};

URL parseURL(const string& url) {
    regex pattern(R"(^(\w+)://([^:/]+)(?::(\d+))?(/.*)?$)");
    smatch match;
    
    if (regex_match(url, match, pattern)) {
        return {
            match[1],  // protocol
            match[2],  // host
            match[3],  // port
            match[4]   // path
        };
    }
    
    return {};
}

int main() {
    auto url = parseURL("https://example.com:8080/path/to/page");
    
    cout << "프로토콜: " << url.protocol << endl;
    cout << "호스트: " << url.host << endl;
    cout << "포트: " << url.port << endl;
    cout << "경로: " << url.path << endl;
}

예시 3: 문자열 치환

#include <regex>

int main() {
    string text = "Hello World, Hello C++";
    regex pattern("Hello");
    
    // 모두 치환
    string result = regex_replace(text, pattern, "Hi");
    cout << result << endl;  // Hi World, Hi C++
    
    // 첫 번째만 치환
    result = regex_replace(text, pattern, "Hi", regex_constants::format_first_only);
    cout << result << endl;  // Hi World, Hello C++
}

예시 4: 로그 파싱

struct LogEntry {
    string timestamp;
    string level;
    string message;
};

vector<LogEntry> parseLog(const string& log) {
    vector<LogEntry> entries;
    
    regex pattern(R"(\[([\d\-: ]+)\] \[(\w+)\] (.+))");
    
    istringstream iss(log);
    string line;
    
    while (getline(iss, line)) {
        smatch match;
        if (regex_match(line, match, pattern)) {
            entries.push_back({
                match[1],  // timestamp
                match[2],  // level
                match[3]   // message
            });
        }
    }
    
    return entries;
}

int main() {
    string log = R"([2026-03-11 10:30:00] [INFO] 서버 시작
[2026-03-11 10:30:05] [ERROR] 연결 실패
[2026-03-11 10:30:10] [WARN] 재시도 중)";
    
    auto entries = parseLog(log);
    
    for (const auto& entry : entries) {
        cout << entry.timestamp << " | " 
             << entry.level << " | " 
             << entry.message << endl;
    }
}

반복자

string text = "abc123def456ghi789";
regex pattern("\\d+");

// 모든 매칭 찾기
sregex_iterator it(text.begin(), text.end(), pattern);
sregex_iterator end;

while (it != end) {
    cout << it->str() << endl;  // 123, 456, 789
    ++it;
}

토큰화

string text = "apple,banana,cherry";
regex delimiter(",");

// 토큰 반복자
sregex_token_iterator it(text.begin(), text.end(), delimiter, -1);
sregex_token_iterator end;

while (it != end) {
    cout << *it << endl;  // apple, banana, cherry
    ++it;
}

자주 발생하는 문제

문제 1: 이스케이프

// ❌ 잘못된 이스케이프
regex pattern("\d+");  // \d가 이스케이프 안됨

// ✅ 이중 백슬래시
regex pattern("\\d+");

// ✅ Raw 문자열 (권장)
regex pattern(R"(\d+)");

문제 2: 성능

// ❌ 매번 regex 생성
for (const string& text : texts) {
    regex pattern("\\d+");  // 비효율
    regex_search(text, pattern);
}

// ✅ regex 재사용
regex pattern("\\d+");
for (const string& text : texts) {
    regex_search(text, pattern);
}

문제 3: 탐욕적 매칭

string html = "<div>content</div>";

// ❌ 탐욕적
regex greedy("<.*>");
// 매칭: <div>content</div> (전체)

// ✅ 비탐욕적
regex nonGreedy("<.*?>");
// 매칭: <div>, </div> (각각)

정규표현식 문법

// 문자 클래스
\d  // 숫자 [0-9]
\w  // 단어 [a-zA-Z0-9_]
\s  // 공백
.   // 모든 문자

// 수량자
*   // 0회 이상
+   // 1회 이상
?   // 0 또는 1회
{n} // 정확히 n회
{n,m}  // n~m회

// 앵커
^   // 시작
$   // 끝
\b  // 단어 경계

// 그룹
()  // 캡처 그룹
(?:)  // 비캡처 그룹

FAQ

Q1: 정규표현식은 언제 사용하나요?

A:

  • 문자열 검증
  • 파싱
  • 검색/치환
  • 데이터 추출

Q2: 성능은?

A: 복잡한 패턴은 느릴 수 있습니다. 간단한 경우 string 메서드가 더 빠릅니다.

Q3: Raw 문자열은?

A: R”(…)”로 백슬래시 이스케이프 불필요.

Q4: ECMAScript vs POSIX?

A: 기본은 ECMAScript. regex_constants로 변경 가능.

Q5: 정규표현식 디버깅은?

A:

  • regex101.com
  • regexr.com
  • 간단한 패턴부터 테스트

Q6: Regex 학습 리소스는?

A:

  • cppreference.com
  • “Mastering Regular Expressions”
  • regex101.com

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ Regex | “정규 표현식” 가이드
  • C++ Regex Iterator | “정규식 반복자” 가이드
  • C++ string_view | “문자열 뷰” C++17 가이드

관련 글

  • C++ Regex |
  • C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
  • C++ Command Pattern 완벽 가이드 | 실행 취소와 매크로 시스템
  • C++ string vs string_view |
  • C++ CRTP 완벽 가이드 | 정적 다형성과 컴파일 타임 최적화