Computer Science/C & C++

[C++] string Class | string 클래스

lww7438 2020. 8. 5. 15:32

string Class

string 클래스

※ C/C++에서는 크게 두 가지 문자열 처리 패러다임이 있다.
1. C-Style Strings
- cstring (string.h) 헤더파일에 C-Style 문자열 처리 함수들이 정의되어 있다.
- C언어에서 익히 사용했던 strcmp(), strcpy() 함수 등이 C-Style 문자열 처리 함수에 해당된다.

2. string Class
- string 헤더파일에 string 클래스의 Specification이 정의되어 있다.
- 문자열 대입/결합/비교, 개별 문자에 대한 접근, 문자열을 구성하는 문자나 부분 문자열에 대한 검색 등을 지원하는
  연산자가 오버로딩 되어있고, 여러 가지 Constructor들과 메서드들이 정의되어 있다.
- string 객체는 Template Specialization(템플릿 특수화): basic_string<char> 가 typedef를 통해 재정의된 형태이다.
- string 클래스는 입력 문자들을 자르지 않고 모두 담을 수 있도록, 객체의 크기를 자동으로 조절하다.


string Class Constructor (string 클래스의 생성자)

※ 관례상, C++에서 Constructor를 약어로 ctor라 표현한다.
- string 객체를 생성할 수 있는 옵션들이다.
- 즉, 문자열을 생성할 수 있는 방법들이다.

* size_type 데이터형
- string 헤더파일에 정의되어 있는, 시스템마다 그 크기가 다른 정수형이다.

* string::npos
- 문자열의 최대 길이를 저장하는 멤버 변수이다.
- 일반적으로 unsigned int의 최대값과 일치한다.

* NBTS (Null Byte Teminated String)
- NULL 문자로 문자열의 끝을 알리는 문자열 처리 방식을 의미하는 약어이다.
- C-Style 문자열 처리 방식에서 다뤄지는 방식이다.
- C++에서도 채택되는 방식이다.

* string 클래스의 Constructor

Constructor Description
string (const char* s) string 객체를 s가 지시하는 NBTS로 초기화한다.
string (size_type n, char c) 문자 c로 모두 초기화된 원소 n개의 string 객체를 생성한다.
string (const string& str) string 객체를 string 객체 str(복사 생성자)로 초기화한다.
string() 크기가 0인 디폴트 string 객체를 생성한다. (디폴트 생성자)
string (const char* s, size_type n) string 객체를 s가 지시하는 NBTS로 초기화하되,
NBTS의 크기를 초과하더라도 n개의 문자까지 진행한다.
template <class Iter>
string (Iter begin, Iter end)
string 객체를 [begin, end)의 범위에 있는 값들로 초기화한다.
begin과 end는 특정 데이터형의 주솟값이어야 하며,
각각은 문자의 위치를 표현하고 있어야 한다.
그 범위는 begin은 포함하고,
end는 포함하지 않는 end 바로 앞 까지를 의미한다.
string (const string& str, size_type pos, size_type n = npos) string 객체를 string 객체 str로 초기화한다.
str에 있는 pos 위치에서 시작해서 str의 끝까지 가거나,
n 문자를 사용하되 str의 끝을 넘어갈 수는 없다.
string (string&& str) noexcept    (C++11) string 객체를 string 객체 str로 초기화한다.
str은 바뀔수 있다. (move 생성자 = 이동 생성자)

※ 컴파일러는 최적화를 위해, 상황에 맞추어 복사 생성자를 사용하거나,
이동 생성자를 사용한다.
string (initializer_list<char> il)     (C++11) string 객체를 초기자 목록 il에 있는 문자로 초기화한다.
string 객체의 리스트 초기화를 가능하게 한다.
#include <iostream>
#include <string>

int main() {
	using namespace std;
    
	string one("Lottery Winner!"); // ctor #1
	cout << one << endl; // overloaded <<
    // Output: Lottery Winner!
    
	string two(20, '$'); // ctor #2
	cout << two << endl;
    // Output: $$$$$$$$$$$$$$$$$$$$
    
	string three(one); // ctor #3
	cout << three << endl;
    // Output: Lottery Winner!
	one += " Oops!"; // overloaded +=
	cout << one << endl;
    // Output: Lottery Winner! Oops!
	two = "Sorry! That was ";
	three[0] = 'P';
    
	string four; // ctor #4
	four = two + three; // overloaded +, = (two와 three가 합쳐진 임시 객체가 four에 대입된다.)
	cout << four << endl;
    // Output: Sorry! That was Pottery Winner!
	
    char alls[] = "All's well that ends well";
    string five(alls,20); // ctor #5
	cout << five << "!\n";
    // Output: All's well that ends!
    
	string six(alls+6, alls + 10); // ctor #6
	cout << six << ", ";
    // Output: well,
	string seven(&five[6], &five[10]); // ctor #6 again
	cout << seven << "...\n";
    // Output: well...
    
	string eight(four, 7, 16); // ctor #7
	cout << eight << " in motion!" << endl;
    // Output: That was Pottery in motion!
    
	return 0;
}

string Class Input (string 클래스의 입력값)

※ C-Style 문자열 처리 방식에서의 3가지 입력 옵션
- C-Style 문자열 처리 함수들은 istream 클래스의 메서드들 이기 때문에, cin 객체의 메서드로써 호출된다.

char info[100];

std::cin >> info;		// 한 단어를 읽는다.
std::cin.getline(info, 100);	// 한 행을 읽되, \n 문자는 버린다.
std::cin.get(info, 100);	// 한 행을 읽되, \n 문자는 큐에 남겨 둔다.
std::cin.getline(info, 100, ':');	// ':'까지 읽고, ':'은 버린다.


// C-Style에서의 >> 연산자 오버로딩 형태
std::cin.operator>>(fname);


* string 클래스에서는 두 가지 입력 옵션을 제공한다.
- string 클래스에서의 문자열 처리 함수들은 독립된 함수들이기 때문에, cin 객체를 콘솔 입력을 위한 매개변수로 사용한다.

string stuff;

std::cin >> stuff;		// 한 단어를 읽는다.
getline(cin, stuff);		// 한 행을 읽되, \n 문자는 버린다.
getline(stuff, ':');		// ':'까지 읽고, ':'은 버린다.


// string 클래스에서의 >> 연산자 오버로딩 형태
operator>>(cin, lname);

- C-Style에서와 달리, string 클래스에서의 getline() 함수는 입력 문자의 개수를 생략할 수 있다.

* string 클래스의 getline()가 문자열의 끝으로 간주하는 요인
1. 파일의 끝을 감지한 경우
- 입력 스트림의 eofbit가 설정된다.
- fail()과 eof() 메서드가 true를 리턴하게 된다.

2. Delimiting Character(구분 문자)를 감지한 경우
- Delimiting Character의 디폴트는 '\n'이다.
- 구분 문자가 감지되면, 구분 문자 이전에 위치한 문자열이 저장되고, 구분 문자는 저장되지 않고 제거된다.
- Delimiter를 '\n'가 아닌, 다른 문자로 지정한 경우, '\n'는 일반 문자가 된다.

3. 가능한 최대의 문자를 수용한 경우
- 해당 시스템의 최대 수용 가능한 문자의 개수는 string::npos, 문자열 대입에 가용되는 메모리의 크기 둘 중 작은 값으로 결정된다.
- 입력 스트림의 failbit가 설정된다.
- fail() 메서드가 true를 리턴하게 된다.

※ Input Stream(입력 스트림) 객체의 Flags
1. eofbit: End-of-File이 감지되면 1로 설정된다.
2. failbit: Input Error(입력 에러)가 감지되면 1로 설정된다.
3. badbit: H/W failure와 같은  인식할 수 없는 오류가 감지되면 1로 설정된다.
4. goodbit: 모든게 정상적으로 동작할 때 1로 설정된다.

* strinf 클래스의 operator>>()
- 구분 문자 이전까지 읽어 들이고, 구분 문자는 제거한다.
- 화이트 스페이스가 감지되면, 화이트 스페이스는 입력 큐에 남겨둔다.
  (화이트 스페이스는 isspace() 함수가 true를 리턴하게 하는 모든 문자를 지칭한다.)

* string 클래스는 파일 입력에도 사용할 수 있다.

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>

int main() {
	using namespace std;
	ifstream fin;
	fin.open("tobuy.txt");
	if (fin.is_open() == false) {
		cerr << "Can't open file. Bye.\n";
		exit(EXIT_FAILURE);
	}
	string item;
	int count = 0;
	getline(fin, item, ':');
	while (fin) { // while input is good 
		++count;
		cout << count <<": " << item << endl;
		getline(fin, item,':');    // fin을 통해 입력되는 문자열을 item에 저장한다. (':'가 입력될 때 까지 모두 입력받으며, ':'은 버린다.)
	}
	cout << "Done\n";
	fin.close();
	return 0;
}

Working with string Class (string 클래스에서 제공하는 기능)

* 문자열 간 비교연산: ==, !=, >, >=, <, <=
- string 클래스에는 문자열에 대한 여섯 가지 비교 연산자가 오버로딩 되어 있다.
- Machine Collating Sequence에 따라, 상대 객체보다 작은 정합 순서를 가진 문자열이 작은 것으로 간주된다.

string snake1("cobra");
string snake2("coral");
char snake3[20] = "anaconda";
if (snake1 < snake 2) // operator<(const string &, const string &)
	...
if (snake1 == snake3) // operator==(const string &, const char *)
	...
if (snake3 != snake2) // operator!=(const char *, const string &)
	...


* 문자열의 크기: size(), length()
- string 클래스의 size() 메서드를 통해 문자열을 구성하는 문자의 개수를 리턴받을 수 있다.
- string 클래스의 length() 메서드 또한 문자열의 크기를 리턴하는 메서드이다.
- length() 메서드는 string 클래스의 구버전부터 사용되어진 메서드이며,
  size() 메서드는 STL 호환성을 위해 추가된 메서드이다.
  (두 메서드는 정확히 같은 동작을 한다.)

* 문자열의 검색: find()
- string::npos는 객체에 저장 가능한 최대 문자 개수이다. (일반적으로 unsigned int 또는 unsigned long의 최댓값과 같다.)
- find()는 해당 부분 문자열을 찾지 못하면, false를 리턴하는 것이 아닌, string::npos를 리턴함에 유의하자.
  (특히 조건문에 사용할 때 유의해야 한다. string::npos는 true로 간주될 것이기 때문이다.)

Method Prototype Description
size_type find(const string& str, size_type pos = 0) const 호출한 문자열의 pos 위치에서부터 시작하여 처음으로 발생하는 부분 문자열 str을 검색한다.
부분 문자열이 발견되면 첫 문자의 인덱스를 리턴한다.
그렇지 않으면 string::npos를 리턴한다.
size_type find(const char* s, size_type pos = 0) const 호출한 문자열의 pos 위치에서부터 시작하여 처음으로 발생하는 부분 문자열 s를 검색한다.
부분 문자열이 발견되면 첫 문자의 인덱스를 리턴한다.
그렇지 않으면 string::npos를 리턴한다.
size_type find(const char* s, size_type pos = 0, size_type n) const 호출한 문자열의 pos 위치에서부터 시작하여 s에 있는 처음 n개의 문자로 구성되는 부분 문자열이 처음 나오는 것을 찾는다. 부분 문자열이 발견되면 첫 문자의 인덱스를 리턴한다.
그렇지 않으면 string::npos를 리턴한다.
size_type find(char ch, size_type pos = 0) const 호출한 문자열의 pos 위치에서부터 시작하여,
문자 ch가 처음 나오는 것을 찾는다.
해당 문자가 발견되면 그것의 인덱스를 리턴한다.
그렇지 않으면 string::npos를 리턴한다.

- find()에 연관된 다른 메서드들은 아래 표와 같다.
  (find()의 Signature와 동일한 Signature를 같는 함수들이다.)

Method Prototype Description
rfind(string) 가장 마지막으로 발생하는 부분 문자열 또는 문자를 찾는다.
find_first_of(string) 호출한 문자열에서 string의 문자들 중 가장 먼저 발견되는 문자의 인덱스를 리턴한다.
find_last_of(string) 호출한 문자열에서 string의 문자들 중 가장 마지막에 발견되는 문자의 인덱스를 리턴한다.
find_first_not_of(string) 호출한 문자열에서 string에 없는 문자들 중 가장 먼저 발견되는 문자의 인덱스를 리턴한다.
find_last_not_of(string) 호출한 문자열에서 string에 없는 문자들 중 가장 마지막에 발견되는 문자의 인덱스를 리턴한다.
string snake1("cobra");

int where = snake1.find_first_of("hark");
// snake1("cobra")에서 "hark"의 문자들 중 가장 먼저 발견되는 문자의 인덱스를 리턴한다.
// 이 경우, where에는 'r'의 인덱스에 해당되는 3이 저장된다.


int where = snake1.find_last_of("hark");
// snake1("cobra")에서 "hark"의 문자들 중 가장 마지막에 발견되는 문자의 인덱스를 리턴한다.
// 이 경우, where에는 'a'의 인덱스에 해당되는 4가 저장된다.


int where = snake1.find_first_not_of("hark");
// snake1("cobra")에서 "hark"의 문자들에 포함되지 않는 문자 중, 가장 먼저 발견되는 문자의 인덱스를 리턴한다.
// 이 경우, where에는 'c'의 인덱스에 해당되는 0이 저장된다.


int where = snake1.find_last_not_of("hark");
// snake1("cobra")에서 "hark"의 문자들에 포함되지 않는 문자 중, 가장 마지막에 발견되는 문자의 인덱스를 리턴한다.
// 이 경우, where에는 'b'의 인덱스에 해당되는 2가 저장된다.

 
* string 클래스의 capacity() 메서드와 reserver(n) 메서드
- capacity() 메서드: 해당 string 객체에 배정된 메모리 블럭의 크기를 리턴한다.
- reserve(size_t n = 0) 메서드: 사용자가 직접 string 객체에 배정될 메모리 블럭의 크기를 지정할 수 있게 한다.

* string 클래스의 c_str() 메서드
- 해당 string 객체의 C-Style 문자열을 가리키는 포인터를 리턴한다.
- C-Style 문자열을 요구하는 파일스트림의 open() 메서드에 유용하게 사용된다.

string filename;
cout << "File Name: ";
cin >> filename;
ofstream fut.open(filename.c_str());

string Varieties (string 다양성)

- 실제 string 문자열 라이브러리는 템플릿 클래스에 기초하고 있으며, 사용자가 사용하는 string 클래스는 단순화된 형태이다.
- 아래 코드에서 볼 수 있다시피, char형 뿐만 아니라, wchar_t, char16_t, char32_t 형에 대한 문자열도 string 클래스가 제공하는 여러 기능들을 적용시킬 수 있다.

template <class charT, class traits = char _traits<charT>, class Allocator = allocator<charT> >
basic_string {
	...
};

// 위 클래스에는 아래와 같은 Aliases들이 있다.
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
typedef basic_string<char16_t> u16string;    // C++11
typedef basic_string<char32_t> u32string;    // C++11