배움 저장소

[홍정모의 따라하며 배우는 C++] 17. std::string 문자열 클래스 본문

Programming Language/C++

[홍정모의 따라하며 배우는 C++] 17. std::string 문자열 클래스

시옷지읏 2022. 1. 2. 18:46

17.1 std::string과 std::wstring


C스타일 문자열보다 편하게 사용할 수 있는 string 클래스와 wstring클래스를 소개한다

 

string과 wstring의 정의를 찾아가보면 아래를 확인할 수 있다.

- 두 클래스 모두 basic_string 클래스를 활용하였고 템플릿 각 자료형으로 Instantiation한 결과이다.

using string  = basic_string<char, char_traits<char>, allocator<char>>;
using wstring = basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t>>;

이 때 w_char은 unsigned short이다. unsigned char보다 많은 문자를 다룰 수 있다. wide-character/unicode를 다룬다

 

wstring을 이용하여 다양한 언어를 출력해보았다

const wstring texts[] = {
    L"Ñá", //Spanish
    L"forêt intérêt", //French
    L"Gesäß", //German
    L"取消波蘇日奇諾", //Chinese
    L"日本人のビット", //Japanese
    L"немного русский", //Russian
    L"ένα κομμάτι της ελληνικής", // Greek
    L"ਯੂਨਾਨੀ ਦੀ ਇੱਕ ਬਿੱਟ", // Punjabi (wtf?). xD
    L"کمی از ایران ", // Persian (I know it, from 300 movie)
    L"కానీ ఈ ఏమి నరకం ఉంది?", //Telugu (telu-what?)
    L"Но какво, по дяволите, е това?" //Bulgarian
};

std::locale::global(std::locale(""));// set to use foreign lang
//std::wcout.imbue(std::locale());    // set wide cout

for (size_t i = 0; i < sizeof(texts) / sizeof(texts[0]); ++i)
    std::wcout << texts[i] << endl;

모든 언어가 출력되진 않는다. 다른 나라 언어팩을 설치하면 작동할 것이다.

위에서 locale::global을 commenting하면 아래 문자열이 출력되지 않는다.

Hello?
Na
foret interet
Gesaß
取消波蘇日奇諾
日本人のビット
немного русский

 

17.2 std::string의 여러가지 생성자들과 형변환


string의 다양한 생성자를 사용하는 예제이다

string str("Default String");
string copied(str); // copy constructor
    
string copied_parts(str,2); // copy parts
cout << copied_parts << endl; // >> fault String

copied_parts = string(str,1,6);
cout << copied_parts << endl; // >> efault

const char* ctyle = "This is C style!";
string converted(ctyle);
cout << converted << endl; // >> This is C style!

converted = string(ctyle, 4); // Warnings! different
cout << converted << endl; // >> This

string initSingle(10, 'A');
cout << initSingle << endl; // >> AAAAAAAAAA

 

STL을 활용하여 string 클래스 사용하기

- 문자를 담는 vector를 사용하여 string 생성자를 호출하고 있다.

- STL algorithm을 활용하여 string 생성자를 호출할 수 있다.

vector<char> v_str;
for(auto &e : "the master of c++")
    v_str.push_back(e);

string copied(v_str.begin(), v_str.end());
cout << copied << endl; // >> the master of c++

copied = string(v_str.begin(), find(v_str.begin(), v_str.end(), 'o'));
cout << copied << endl; // >> the master

 

일반 데이터형을 string 클래스로 변환하기

to_string(1);   // int -> str
to_string(1.0f);// float -> str
to_string(1u);  // unsigned -> str

string converted(std::to_string(1.1));
cout << converted << endl; // >> 1.100000

converted += to_string(1.2);
cout << converted << endl; // >> 1.1000001.200000

 

string 클래스를 일반 데이터형으로 변환하기

int i = stoi("10");
float f = stof("10.23");
cout << i << endl; // >> 10
cout << f << endl; // >> 10.23

string str_d = to_string(1.0);
double d = stod(str_d);
cout << d << endl; // >> 1

 

Istream과 ostream을 활용하여 문자열과 일반형을 변환해보자

- Istream과 Ostream은 18장에서 배운다. 형변환에 사용할 수 있음을 확인하고 넘어가자. GUI에 활용하면 편하다.

- 일반형은 문자열로 항상 바꿀 수 있지만. 문자열은 일반형으로 항상 바꿀 수 없다. 

 

ostringstream 활용하기: 일반형 -> 문자열

- ostringstream 클래스는 멤버함수로 .str( )을 가지고 있다. 어떤 값이 입력되든 string 클래스로 변환하여 반환한다.

template <typename T>
string ToString(T t) {
    std::ostringstream osstream; // #include <sstream>
    osstream << t;
    return osstream.str();
}

 

Istringstream 활용하기: 문자열 -> 일반형

- "isstream >> t"에서 연산자 >>는 가지고 있는 문자열을 t 자료형에 맞게 넣어준다. 변환에 실패하면 false를 반환한다.

template <typename T>
bool FromString(const string& s, T& t) { // Outer Parameter
    istringstream isstream(s);  // intput string stream
    return (isstream >> t)? true : false; // convert str to T
}

int main() {
    double d;
    bool check = FromString("3.141592", d);
    if(check)
        cout << typeid(d).name() << d << endl; // >> double3.14159
    else
        cout << "Failed" << endl;
}

 

17.3 std::string의 길이와 용량


동적할당 메모리를 사용하는 STL vector에서 capacity와 size는 다르다. string 클래스도 이와 동일하다.

효율적으로 사용하기 위해 memory reallocation을 최소화하자.

 

string 자료형은 끝에 null character가 없다. 그래서 string의 크기를 확인해보면 입력한 문자열 개수만큼이다

- string 클래스 내부에 문자열 개수를 저장하고 있기 때문에 null character까지 출력할 필요가 없다.

- capacity( )를 확인해보자. new와 delete를 사용하면 느려지기 때문에 미리 여유분을 더 할당받아 사용하고 있다. 

string size_test("12345678");
cout << size_test.size() << endl;    // >> 8
cout << size_test.capacity() << endl;// >> 15

size_test = string("");
cout << size_test.size() << endl;    // >> 0
cout << size_test.capacity() << endl;// >> 15

- const char*로 저장되는 문자배열은 항상 끝에 null character를 덧붙인다. string 클래스는 내부적으로 해당 null character를 저장하지 않는다. 변수명 size_test에 "" 값을 넣는 코드를 보자. ""은 const char[1]로 표시되지만 string class 내부에 저장되지 않는다.

 

원하는 만큼 동적할당 메모리를 받으려면 reserve 멤버함수를 사용하자. 매개변수 이상의 공간을 할당받는다

string capacity_test("12345678");
capacity_test.reserve(100);
cout << capacity_test.capacity() << endl; // >> 111

 

아래 max_size( ) 멤버함수는 string 클래스가 가질 수 있는 문자열 개수의 최대치를 반환한다.

cout << size_test.max_size() << endl;// >> 2147483647

 

17.4 문자 접근하기와 배열로의 변환


string str("abcdefg");
cout << str[0] << endl; // >> a
cout << str[3] << endl; // >> d

str[3] = 'D';

cout << str << endl;    // >> abcDefg

 

string 클래스에서 예외처리 구현하기

- string 클래스에서 구현된 [ ] 연산자는 성능을 위하여 예외처리를 구현하지 않았다. 예외처리를 사용하려면 at( ) 멤버함수를 사용하자. throw를 던진다. [ ]연산자를 사용하되 예외처리를 하려면 assert를 활용해줌이 적절하다.

- 이 같은 사례는 vector에서도 동일하게 적용된다.

string str("abcdefg");
try {
    //str[100] = 'X';  // Just Stop. No exception
    str.at(100) = 'X'; // Implemented with throw
}
catch (exception& e) {
    cout << e.what() << endl;
}
>> invalid string position

 

C-style 배열 사용하기 예제

/* C - Style string example */
char* ctyleStr = new char[7];
strcpy_s(ctyleStr, 7, "Hello?");
cout << ctyleStr << endl;

 

C-Style 배열 포인터로 변환하기

- string 클래스의 멤버함수 data와 c_str은 차이가 없다. 모두 해당 문자열에 null character를 추가하여 반환한다.

string str("abcdefg");

//const char* arr = str.data();
const char* arr = str.c_str(); // same as above

for(unsigned i=0; i<8; ++i)
    cout << (int)arr[i] << " " << arr[i] << endl;
97 a
98 b
99 c
100 d
101 e
102 f
103 g
0

 

C-Style 문자 배열로 변환하기

- string 클래스의 멤버함수 copy를 사용하면 문자배열을 복사한다. 이때 null character는 추가하지 않는다

string str("abcdefg");

char buf[20];
str.copy(buf, 7,1); // without add null character
buf[7] = '\0'; // do manually
cout << buf << endl;
bcdefg

 

17.5 string 대입, 교환, 덧붙이기, 삽입

append


string 클래스 내부에 구현되어있는 오버로딩 함수를 잘 활용하자. 생각할 수 있는 대부분이 구현되어있다. 

 

string 클래스의 대입연산자 사용해보기

- string 클래스에서 구현된 멤버함수는 자기 자신을 반환하기 때문에 chaining을 사용할 수 있다. 아래에서 assign ( ) 멤버함수를 호출한 뒤 다시 append( ) 멤버함수를 호출한다.

string str("constructor and assignment operator");

string copied;
copied = str;
copied = "assignmnet operator";
copied.assign("member function assign()").append(" Good!");

cout << copied << endl; // >> member function assign() Good!

string 클래스 멤버함수에 다양한 오버로딩이 구현되어 있다. 위 사례에서 assign 멤버함수에 Iterator를 사용할 수 있다.

 

swap 사용해보기

string one("ONE");
string two("TWO");

swap(one, two);
cout << one << " " << two << endl; // >> TWO ONE
one.swap(two);
cout << one << " " << two << endl; // >> ONE TWO

 

덧붙이기 활용하기

string one("ONE");

one.append("TWO");
one += "THREE";
one = one + "FOUR";
one.push_back('!'); // character only

cout << one << endl; // >> ONETWOTHREEFOUR!

 

삽입 활용하기

string get = "---><---";
get.insert(find(get.begin(),get.end(), '<'), 'X');

cout << get << endl; // >> --->X<---

 

Comments