Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>
표준 C++ 라이브러리 string class

7. 표준 C++ 라이브러리 string class

위에 언급된 String class (S가 대문자인 것에 주의!)는 Java를 사용하는 사람들을 위한 것인 반면, 표준 C++ 라이브러리에서 제공되는 "진짜" string class를 주목할 필요가 있다.

string class는 C에서의 가장 큰 문제점 중 하나인 문자배열의 단점을 극복하기 위해 만들어졌다. 문자배열이 무척 빠르긴 하지만, 많은 단점을 갖고 있다. 문자배열은 많은 버그의 원인이고, 이를 parsing하는 일은 굉장히 귀찮은 일이다.

string class는 문자열을 파싱하고, 조정하는데 필요한 좋은 인터페이스를 제공하고, STL과도 호환가능하다. 즉, 모든 STL의 알고리즘을 사용할 수 있다. 실제로 문자열은 vector<char> ( 문자들을 위한 container 혹은 진보된 문자배열 ) 로 취급될 수 있다.

다음의 사이트에서 참고할만한 것들을 얻을 수 있다:

7.1. 예제로 살펴보는 string

string을 만드는 것은 쉽다. Creating a string is easy:

#include <string>
#include <iostream>

using namespace std;

int main()
{
    string str("Hello World!"); // 혹은 string str = "Hello World!";
    cout << str << endl;
}

이 코드는 "str'란 string을 만들고, "Hello World!' 라는 내용을 넣을 것이다. 그리고 cout을 사용하여 표준출력(stdout)으로 출력할 것이다.

(이제부터 모든 헤더와 namespace 관련부분은 생략할 것이다.)

문자열의 부분을 구하는 것 역시 쉽다 :

string str("Hello Universe!");
string start = str.substr(0, 5);
string end = str.substr(5);

여기서는 첫 6글자를 string "start"에, 나머지는 "end"에 들어갈 것이다.

문자열의 길이를 얻기 위해서는, 아래와 같이 하면 된다.

string str("How long is this string?");
cout << "Length of string is: " << str.size() << endl;

혹은 이와 정확히 똑같은 역할을 하는 length() 를 써도 된다.

7.2. 문자열을 찾기

문자열을 찾는 것은 문자배열보다 훨씬 쉽다. string class는 문자열을 찾는데 효율적인 멤버 함수들을 제공한다. 모든 멤버함수는 string::size_type 을 return한다.

표 1. 문자열 검색 멤버 함수

멤버 함수작동 
find() 주어진 부분문자열이 처음으로 나타나는 곳을 찾는다 
find_first_of() find()와 같으나 주어진 문자가 처음으로 나타나는 위치를 찾는다 
find_last_of() find first of()와 같으나, 주어진 문자가 마지막으로 나타나는 위치를 찾는다 
find_first_not_of() find first of() 과 같으나 주어진 문자가 아닌 첫 문자가 처음으로 나타나는 위치를 찾는다 
find_last_not_of() find last of() 과 같으나 주어진 문자가 아닌 문자를 찾는다. 
rfind() find()와 같으나, 찾는 방향이 반대이다. (뒤쪽부터 찾는다) 
   

가장 흔한 상황은 문자열을 찾는 것이고, 이는 find() 함수를 사용하면 된다.

string str("Hello, can you find Ben?");
string::size_type position = str.find("Ben");
cout << "First occurence of Ben was found at: " << position << endl;

이 코드는 'Ben'에 대해 대소문자를 구별하는 검색을 하고, 시작위치를 'position'에 string::size_type 타입으로 넣는다. 리턴하는 값이 int가 아니라 특별히 고안된 string::size_type 타입이라는데 주의하라.

find_first_of() 함수는 실제적인 예가 필요할 것이다. 아래와 같은 상황을 보자.

string s = "C++ is an impressive language.";
string::size_type pos = s.find_first_of(" .");

while (pos != string::npos) {
    cout << "Found space or dot at: " << pos << endl;
    pos = s.find_first_of(" .", pos + 1);
}

find_first_of()함수를 쓰면, 우리는 첫번째 인자의 모든 문자를 찾게 되고, 따라서 여기서는 스페이스(' ') 혹은 점('.')을 찾게 된다.

프로그램을 컴파일해서 어떻게 출력되는지 보아라.

7.3. string tokenizer

문자열을 가지고 자주 하게되는 작업 중 하나는, 어떤 구분자를 가지고 토큰들로 나누는 것이다 (tokenize). tokenizer는 문자열을 find()를 계속 부르는 일 없이 쉽게 조그만 조각들로 쪼갤 수 있도록 해준다. C에서는, 아마도 문자 배열에 대해 strtok() 란 함수를 썼을 것이지만, 문자열에 대해서는 이러한 함수가 없다. 따라서 직접 이런 함수를 만들어야 겠지만, 몇가지 해결책이 있다.

The advanced tokenizer:

void Tokenize(const string& str,
                      vector<string>& tokens,
                      const string& delimiters = " ")
{
    // 맨 첫 글자가 구분자인 경우 무시
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // 구분자가 아닌 첫 글자를 찾는다
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // token을 찾았으니 vector에 추가한다
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // 구분자를 뛰어넘는다.  "not_of"에 주의하라
        lastPos = str.find_first_not_of(delimiters, pos);
        // 다음 구분자가 아닌 글자를 찾는다
        pos = str.find_first_of(delimiters, lastPos);
    }
}

tokenizer는 다음과 같이 쓰일 수 있다.

#include <string>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
    vector<string> tokens;

    string str("Split me up! Word1 Word2 Word3.");

    Tokenize(str, tokens);

    copy(tokens.begin(), tokens.end(), ostream_iterator<string>(cout, ", "));
}

위의 코드는 Tokenize 함수를 사용하는 예로서, 첫번째 인자인 str를 쪼갠다. 그리고 우리가 세 번째 인자를 주지 않았기 때문에 디폴트로 설정된 " "(spacebar)를 구분자로 사용한다. 그리고 모든 element는 tokens 벡터에 들어가게 될 것이다.

마지막으로 표준 출력에 벡터 전체를 copy()함으로써 벡터의 내용을 화면으로 볼 수 있을 것이다.

또다른 접근 방법은 stringstream을 사용하는 것이다. C++에서 stream은 특수한 기능이 하나 있는데, 이는 공백(whitespace)를 만날 때까지 읽기를 계속한다는 것이다. 따라서 아래의 코드는 공백을 기준으로 문자열을 나누고자 할 때 잘 동작할 것이다.

#include <vector>
#include <string>
#include <sstream>

using namespace std;

int main()
{
    string str("Split me by whitespaces");
    string buf; // 버퍼 string
    stringstream ss(str); // string을 stream에 넣는다

    vector<string> tokens; // word들을 넣을 vector

    while (ss >> buf)
        tokens.push_back(buf);
}

이제 stringstream은 출력 연산자(>>)를 사용 하여 문자열을 buf 에 공백을 만날 때마다 넣는다. buf는 이를 차례대로 벡터에 push_back() 한다. 그리고 이제 tokens 벡터는 str 에 들어있는 모든 단어를 갖게 된다.