STL string & regex
std::string은 C++에서 가장 자주 사용되는 컨테이너 중 하나입니다. 내부 SSO(Small String Optimization)와 힙 할당 동작을 이해하면 불필요한 복사를 줄일 수 있습니다. std::regex는 C++11에서 표준화된 정규표현식 라이브러리로, 파싱·검증·치환 작업에 활용됩니다.
1. std::string 핵심 동작
섹션 제목: “1. std::string 핵심 동작”1.1 SSO (Small String Optimization)
섹션 제목: “1.1 SSO (Small String Optimization)”대부분의 STL 구현(libstdc++, MSVC STL)은 짧은 문자열을 힙 할당 없이 객체 내부 버퍼에 저장합니다.
#include <string>#include <iostream>
std::string small = "hello"; // SSO: 힙 할당 없음 (≤15자, 구현마다 다름)std::string large = std::string(100, 'x'); // 힙 할당 발생
// capacity와 size 확인std::string s;s.reserve(100); // 미리 힙 할당해 재할당 방지for (int i = 0; i < 100; i++) s += 'a'; // reserve 없으면 여러 번 재할당 발생1.2 자주 쓰는 메서드
섹션 제목: “1.2 자주 쓰는 메서드”std::string s = "Hello, World!";
// 검색s.find("World"); // 7 (없으면 std::string::npos)s.rfind('l'); // 10 (뒤에서 검색)s.find_first_of("aeiou"); // 1 ('e')s.find_first_not_of("Helo, "); // 7 ('W')
// 부분 문자열s.substr(7, 5); // "World"
// 수정s.replace(7, 5, "C++"); // "Hello, C++!"s.erase(5, 2); // "HelloWorld!" (", " 제거)s.insert(5, ", "); // "Hello, World!"
// 변환std::string num = std::to_string(42);int n = std::stoi("123");double d = std::stod("3.14");
// 대소문자 (표준 없음, 수동 처리)std::transform(s.begin(), s.end(), s.begin(), ::tolower);1.3 문자열 분할 (C++20 이전)
섹션 제목: “1.3 문자열 분할 (C++20 이전)”std::vector<std::string> Split(const std::string& str, char delim){ std::vector<std::string> tokens; std::string token; std::istringstream stream(str); while (std::getline(stream, token, delim)) tokens.push_back(std::move(token)); return tokens;}
auto parts = Split("a,b,c,d", ',');// {"a", "b", "c", "d"}2. std::regex 기초
섹션 제목: “2. std::regex 기초”2.1 패턴 매칭
섹션 제목: “2.1 패턴 매칭”#include <regex>#include <string>#include <iostream>
std::regex emailPattern(R"([\w.]+@[\w.]+\.[a-z]{2,})");
// 전체 문자열이 패턴과 일치하는지if (std::regex_match(email, emailPattern)) std::cout << "유효한 이메일\n";
// 문자열 일부에서 패턴 탐색std::string log = "[2024-01-15] Error: file not found";std::regex datePattern(R"(\d{4}-\d{2}-\d{2})");std::smatch match;if (std::regex_search(log, match, datePattern)) std::cout << "날짜: " << match[0] << "\n"; // "2024-01-15"2.2 캡처 그룹
섹션 제목: “2.2 캡처 그룹”std::string url = "https://www.example.com:8080/path";std::regex urlPattern(R"((https?)://([^/:]+)(?::(\d+))?(/.*)?)");std::smatch m;
if (std::regex_match(url, m, urlPattern)){ std::cout << "프로토콜: " << m[1] << "\n"; // "https" std::cout << "호스트: " << m[2] << "\n"; // "www.example.com" std::cout << "포트: " << m[3] << "\n"; // "8080" std::cout << "경로: " << m[4] << "\n"; // "/path"}2.3 치환
섹션 제목: “2.3 치환”std::string text = "The year 2023 and 2024 are recent";std::regex yearPattern(R"(\d{4})");
// 모든 연도를 "YYYY"로 치환std::string result = std::regex_replace(text, yearPattern, "YYYY");// "The year YYYY and YYYY are recent"
// 캡처 그룹을 활용한 치환std::string csv = "Smith,John,42";std::regex csvPattern(R"((\w+),(\w+),(\d+))");std::string formatted = std::regex_replace(csv, csvPattern, "$2 $1 (age: $3)");// "John Smith (age: 42)"2.4 반복 탐색 (regex_iterator)
섹션 제목: “2.4 반복 탐색 (regex_iterator)”std::string data = "apple 3, banana 5, cherry 2";std::regex itemPattern(R"((\w+)\s+(\d+))");
auto begin = std::sregex_iterator(data.begin(), data.end(), itemPattern);auto end = std::sregex_iterator();
for (auto it = begin; it != end; ++it){ const std::smatch& m = *it; std::cout << m[1] << ": " << m[2] << "\n";}// apple: 3// banana: 5// cherry: 23. regex 성능 주의사항
섹션 제목: “3. regex 성능 주의사항”std::regex는 런타임에 패턴을 컴파일합니다. 핫 경로에서 매번 std::regex 객체를 생성하면 심각한 성능 저하가 발생합니다.
// 나쁜 예: 매 호출마다 패턴 컴파일bool IsValidEmail(const std::string& email){ std::regex pattern(R"([\w.]+@[\w.]+\.[a-z]{2,})"); // 매번 컴파일! return std::regex_match(email, pattern);}
// 좋은 예: 정적 또는 멤버 변수로 한 번만 컴파일bool IsValidEmail(const std::string& email){ static const std::regex pattern(R"([\w.]+@[\w.]+\.[a-z]{2,})"); return std::regex_match(email, pattern);}성능이 중요한 프로덕션 코드라면 std::regex 대신 수동 파싱이나 서드파티 라이브러리(PCRE2, RE2)를 검토합니다.
4. C++20 ranges와 string
섹션 제목: “4. C++20 ranges와 string”#include <ranges>#include <string>
std::string s = " hello world ";
// C++20: 공백 제거auto trimmed = s | std::views::drop_while(::isspace) | std::views::reverse | std::views::drop_while(::isspace) | std::views::reverse;
// 문자열 뷰로 분할 (C++20 split_view)std::string csv = "a,b,c,d";for (auto token : csv | std::views::split(',')){ std::string_view sv(token.begin(), token.end()); std::cout << sv << '\n';}std::string은 SSO로 짧은 문자열을 힙 없이 저장하므로, 짧은 문자열 연산은 생각보다 빠르다.- 반복적으로
+=를 사용할 때는reserve()로 재할당을 방지한다. std::regex객체는 생성 비용이 높으므로static또는 멤버 변수로 한 번만 컴파일한다.- 캡처 그룹은
std::smatch의 인덱스 또는$1,$2로 접근하며, 치환 시$&는 전체 매치를 의미한다.