콘텐츠로 이동

C++ 문자열 라이브러리 완벽 가이드

개요

C++의 string 클래스와 관련 라이브러리들은 안전하고 효율적인 문자열 처리를 제공합니다.

목차


기본 string 클래스

string 헤더

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

// 기본 사용법
string str = "Hello World";
cout << str << endl;

// 문자열 길이
cout << str.length() << endl;  // 11
cout << str.size() << endl;    // 11 (length()와 동일)

// 빈 문자열 확인
if (str.empty()) {
    cout << "String is empty" << endl;
}

// 용량 관련
cout << str.capacity() << endl; // 할당된 메모리 크기
str.reserve(100);              // 최소 100자 용량 예약
str.shrink_to_fit();           // 불필요한 메모리 해제

문자 접근

string str = "Hello";

// 인덱스 접근
char ch = str[0];        // 'H' (범위 검사 없음)
char ch = str.at(0);     // 'H' (범위 검사 있음)

// 첫/마지막 문자
char first = str.front(); // 'H'
char last = str.back();   // 'o'

// 반복자 사용
for (auto it = str.begin(); it != str.end(); ++it) {
    cout << *it << " ";
}

// 범위 기반 for 루프
for (char c : str) {
    cout << c << " ";
}

문자열 생성과 초기화

다양한 생성 방법

// 기본 생성
string s1;                      // 빈 문자열
string s2("Hello");             // C 스타일 문자열로 초기화
string s3 = "World";            // 복사 초기화
string s4(5, 'A');              // "AAAAA" (문자 5개)
string s5(s2);                  // 복사 생성자
string s6 = s2;                 // 복사 생성자

// 부분 문자열로 초기화
string s7(s2, 1, 3);            // s2의 1번째부터 3글자 "ell"
string s8(s2.begin() + 1, s2.end() - 1); // 반복자 범위

// C++11 초기화 리스트
string s9{'H', 'e', 'l', 'l', 'o'};

// 이동 생성자 (C++11)
string s10 = move(s2);          // s2의 내용을 s10으로 이동

문자열 할당

string str;

str = "Hello";                  // 할당
str = 'A';                      // 단일 문자 할당
str = other_string;             // 다른 문자열 할당

str.assign("World");            // assign 함수
str.assign(5, 'B');             // "BBBBB"
str.assign(other_string, 2, 3); // 부분 할당

문자열 조작

추가 연산

string str = "Hello";

// 뒤에 추가
str += " World";                // "Hello World"
str.append(" Again");           // "Hello World Again"
str.append(3, '!');             // "Hello World Again!!!"

// 특정 위치에 삽입
str.insert(5, " C++");          // "Hello C++ World Again!!!"
str.insert(0, "Hi ");           // "Hi Hello C++ World Again!!!"

// 연결 연산자
string result = str + " " + "End"; // 문자열 연결

제거 연산

string str = "Hello World Programming";

// 특정 위치 제거
str.erase(5, 6);                // 5번째부터 6글자 제거 "HelloProgramming"
str.erase(5);                   // 5번째부터 끝까지 제거 "Hello"

// 문자 제거
str.pop_back();                 // 마지막 문자 제거 "Hell"

// 전체 제거
str.clear();                    // 빈 문자열로 만들기

교체 연산

string str = "Hello World Hello";

// 부분 교체
str.replace(6, 5, "C++");       // "Hello C++ Hello"
str.replace(str.find("Hello"), 5, "Hi"); // 첫 번째 "Hello"를 "Hi"로

// 전체 교체 (C++에는 built-in 없음, 직접 구현)
void replace_all(string& str, const string& from, const string& to) {
    size_t pos = 0;
    while ((pos = str.find(from, pos)) != string::npos) {
        str.replace(pos, from.length(), to);
        pos += to.length();
    }
}

replace_all(str, "Hello", "Hi"); // 모든 "Hello"를 "Hi"로

부분 문자열

string str = "Hello World Programming";

// substr 함수
string sub1 = str.substr(6);        // "World Programming"
string sub2 = str.substr(6, 5);     // "World"
string sub3 = str.substr(0, 5);     // "Hello"

// 복사 함수
char buffer[100];
str.copy(buffer, 5, 0);         // str의 0번째부터 5글자를 buffer에 복사
buffer[5] = '\0';               // null 종료 문자 추가

문자열 검색

기본 검색 함수

string str = "Hello World Programming Hello";

// find: 첫 번째 발견 위치
size_t pos = str.find("World");         // 6
size_t pos = str.find("World", 10);     // 10번째부터 검색
size_t pos = str.find('o');             // 4 (첫 번째 'o')

// rfind: 마지막 발견 위치 (뒤에서부터 검색)
size_t pos = str.rfind("Hello");        // 25 (마지막 "Hello")
size_t pos = str.rfind('o');            // 28 (마지막 'o')

// 검색 실패 확인
if (pos == string::npos) {
    cout << "Not found" << endl;
}

고급 검색 함수

string str = "Hello123World456";

// find_first_of: 문자 집합 중 아무나 첫 번째
size_t pos = str.find_first_of("0123456789"); // 5 (첫 번째 숫자)
size_t pos = str.find_first_of("aeiou");       // 1 (첫 번째 모음)

// find_last_of: 문자 집합 중 아무나 마지막
size_t pos = str.find_last_of("0123456789");  // 13 (마지막 숫자)

// find_first_not_of: 문자 집합에 포함되지 않는 첫 번째
size_t pos = str.find_first_not_of("Helo");   // 5 (첫 번째 숫자)

// find_last_not_of: 문자 집합에 포함되지 않는 마지막
size_t pos = str.find_last_not_of("0123456789"); // 9 (마지막 알파벳)

비교 함수

string str1 = "Hello";
string str2 = "World";

// 비교 연산자
if (str1 == str2) { /* 같음 */ }
if (str1 != str2) { /* 다름 */ }
if (str1 < str2)  { /* 사전순으로 앞 */ }
if (str1 > str2)  { /* 사전순으로 뒤 */ }

// compare 함수
int result = str1.compare(str2);  // 음수: str1 < str2, 0: 같음, 양수: str1 > str2
int result = str1.compare(0, 3, str2, 0, 3); // 부분 비교

// 대소문자 무시 비교 (C++에는 built-in 없음)
bool case_insensitive_compare(const string& a, const string& b) {
    return equal(a.begin(), a.end(), b.begin(), b.end(),
                 [](char a, char b) {
                     return tolower(a) == tolower(b);
                 });
}

문자열 변환

숫자 ↔ 문자열 변환 (C++11)

#include <string>

// 문자열 → 숫자
string num_str = "123";
int num = stoi(num_str);              // 123
long long_num = stol(num_str);        // 123L
float float_num = stof("3.14");       // 3.14f
double double_num = stod("3.14159");  // 3.14159

// 진법 지정
int hex_num = stoi("ff", nullptr, 16); // 255 (16진법)
int bin_num = stoi("1010", nullptr, 2); // 10 (2진법)

// 숫자 → 문자열
int num = 123;
string str = to_string(num);          // "123"
double pi = 3.14159;
string pi_str = to_string(pi);        // "3.141590"

대소문자 변환

#include <algorithm>
#include <cctype>

string str = "Hello World";

// 대문자로 변환
transform(str.begin(), str.end(), str.begin(), ::toupper);
// str은 이제 "HELLO WORLD"

// 소문자로 변환
transform(str.begin(), str.end(), str.begin(), ::tolower);
// str은 이제 "hello world"

// 첫 글자만 대문자
string capitalize(string str) {
    if (!str.empty()) {
        str[0] = toupper(str[0]);
        transform(str.begin() + 1, str.end(), str.begin() + 1, ::tolower);
    }
    return str;
}

공백 제거 (trim)

#include <algorithm>
#include <cctype>

// 왼쪽 공백 제거
string ltrim(string str) {
    str.erase(str.begin(), find_if(str.begin(), str.end(), 
                                   [](unsigned char ch) {
                                       return !isspace(ch);
                                   }));
    return str;
}

// 오른쪽 공백 제거
string rtrim(string str) {
    str.erase(find_if(str.rbegin(), str.rend(), 
                      [](unsigned char ch) {
                          return !isspace(ch);
                      }).base(), str.end());
    return str;
}

// 양쪽 공백 제거
string trim(string str) {
    return ltrim(rtrim(str));
}

정규표현식

regex 헤더 (C++11)

#include <regex>
#include <string>
#include <iostream>

string text = "Email: john@example.com, Phone: 123-456-7890";

// 기본 매칭
regex email_pattern(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
smatch match;

if (regex_search(text, match, email_pattern)) {
    cout << "Found email: " << match[0] << endl; // john@example.com
}

// 전체 매칭 확인
regex full_pattern(R"(^[a-zA-Z]+$)"); // 알파벳만
if (regex_match("Hello", full_pattern)) {
    cout << "Valid alphabet string" << endl;
}

정규식 검색과 교체

#include <regex>

string text = "The price is $123.45 and $67.89";

// 모든 매치 찾기
regex price_pattern(R"(\$([0-9]+\.[0-9]{2}))");
sregex_iterator start(text.begin(), text.end(), price_pattern);
sregex_iterator end;

for (sregex_iterator i = start; i != end; ++i) {
    smatch match = *i;
    cout << "Price: " << match[1] << endl; // 123.45, 67.89
}

// 교체
string result = regex_replace(text, price_pattern, "[$1]");
// "The price is [123.45] and [67.89]"

유용한 정규식 패턴

// 이메일 검증
regex email_regex(R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");

// 전화번호 (XXX-XXX-XXXX)
regex phone_regex(R"(\d{3}-\d{3}-\d{4})");

// IP 주소
regex ip_regex(R"(\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b)");

// URL
regex url_regex(R"(https?://[^\s]+)");

// 숫자 (정수, 소수)
regex number_regex(R"(-?\d+(?:\.\d+)?)");

// 단어 경계
regex word_regex(R"(\b\w+\b)");

성능 최적화

문자열 연결 최적화

// 비효율적 (매번 새 문자열 생성)
string result;
for (int i = 0; i < 1000; ++i) {
    result += "text";
}

// 효율적 1: reserve 사용
string result;
result.reserve(4000); // 예상 크기 미리 할당
for (int i = 0; i < 1000; ++i) {
    result += "text";
}

// 효율적 2: stringstream 사용
ostringstream oss;
for (int i = 0; i < 1000; ++i) {
    oss << "text";
}
string result = oss.str();

문자열 뷰 (C++17)

#include <string_view>

// string_view는 문자열을 복사하지 않고 참조만
void process_string(string_view sv) {
    cout << sv << endl; // 복사 비용 없음
}

string str = "Hello World";
process_string(str);        // string에서 변환
process_string("literal");  // 리터럴에서 변환
process_string(str.substr(0, 5)); // 부분 문자열도 복사 없음

이동 의미론 활용

// C++11 이동 생성자 활용
string create_string() {
    string result = "Long string content...";
    return result; // RVO 또는 이동으로 최적화
}

string str = create_string(); // 복사 없이 이동

// 명시적 이동
string source = "Original";
string dest = move(source); // source는 이제 빈 상태

실전 예제

1. 문자열 분할 (Split)

#include <vector>
#include <sstream>

vector<string> split(const string& str, char delimiter) {
    vector<string> tokens;
    stringstream ss(str);
    string token;

    while (getline(ss, token, delimiter)) {
        tokens.push_back(token);
    }

    return tokens;
}

// 사용 예
string csv = "apple,banana,cherry";
vector<string> fruits = split(csv, ',');
// {"apple", "banana", "cherry"}

2. 문자열 조인

string join(const vector<string>& strings, const string& delimiter) {
    if (strings.empty()) return "";

    ostringstream oss;
    oss << strings[0];

    for (size_t i = 1; i < strings.size(); ++i) {
        oss << delimiter << strings[i];
    }

    return oss.str();
}

// 사용 예
vector<string> words = {"Hello", "World", "C++"};
string sentence = join(words, " "); // "Hello World C++"

3. 문자열 템플릿 엔진

#include <map>

class StringTemplate {
private:
    string template_str;
    map<string, string> variables;

public:
    StringTemplate(const string& tmpl) : template_str(tmpl) {}

    void set(const string& key, const string& value) {
        variables[key] = value;
    }

    string render() {
        string result = template_str;

        for (const auto& [key, value] : variables) {
            string placeholder = "{{" + key + "}}";
            replace_all(result, placeholder, value);
        }

        return result;
    }

private:
    void replace_all(string& str, const string& from, const string& to) {
        size_t pos = 0;
        while ((pos = str.find(from, pos)) != string::npos) {
            str.replace(pos, from.length(), to);
            pos += to.length();
        }
    }
};

// 사용 예
StringTemplate tmpl("Hello {{name}}, you are {{age}} years old!");
tmpl.set("name", "John");
tmpl.set("age", "25");
cout << tmpl.render(); // "Hello John, you are 25 years old!"

4. 문자열 유틸리티 클래스

class StringUtils {
public:
    // 문자열이 특정 문자열로 시작하는지 확인
    static bool starts_with(const string& str, const string& prefix) {
        return str.length() >= prefix.length() &&
               str.compare(0, prefix.length(), prefix) == 0;
    }

    // 문자열이 특정 문자열로 끝나는지 확인
    static bool ends_with(const string& str, const string& suffix) {
        return str.length() >= suffix.length() &&
               str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
    }

    // 문자열 패딩
    static string pad_left(const string& str, size_t width, char fill = ' ') {
        if (str.length() >= width) return str;
        return string(width - str.length(), fill) + str;
    }

    static string pad_right(const string& str, size_t width, char fill = ' ') {
        if (str.length() >= width) return str;
        return str + string(width - str.length(), fill);
    }

    // 문자열 반복
    static string repeat(const string& str, size_t count) {
        string result;
        result.reserve(str.length() * count);
        for (size_t i = 0; i < count; ++i) {
            result += str;
        }
        return result;
    }

    // 문자열 뒤집기
    static string reverse(string str) {
        std::reverse(str.begin(), str.end());
        return str;
    }

    // 카멜케이스를 스네이크케이스로
    static string camel_to_snake(const string& camel) {
        string result;
        for (char c : camel) {
            if (isupper(c)) {
                if (!result.empty()) result += '_';
                result += tolower(c);
            } else {
                result += c;
            }
        }
        return result;
    }

    // 스네이크케이스를 카멜케이스로
    static string snake_to_camel(const string& snake) {
        string result;
        bool next_upper = false;

        for (char c : snake) {
            if (c == '_') {
                next_upper = true;
            } else {
                result += next_upper ? toupper(c) : c;
                next_upper = false;
            }
        }

        return result;
    }
};

마지막 업데이트: 2025-06-24