728x90

c언어에서 쓰는 cstring은 문자열이 아니라 문자(char)의 배열인데 끝에 '\0'(널)로 막아줘야 하는 등 불편하고 문제가 많아서 C++에서는 string 클래스를 사용해서 문자열을 처리한다.

string은 int, double과 같은 기초타입(basic type)이 아니라 클래스이다.

따라서 string 클래스를 사용하고 싶으면 #include <string> 문장을 써서 <string> 헤더파일을 포함해야 한다.

string 클래스는 문자의 배열을 멤버변수로 가지며, 그것을 처리하는 함수들을 멤버함수로 가지고 있다.


아래의 두 문장은 결과적으로는 같지만 첫 번째 문장이 더 빠르다.

string s("Welcome to c++");
string s = "Welcome to c++";

첫 번째 문장은 바로 문자열 객체를 생성하는 것이지만

두 번째 문장이 동작하는 과정은 다음과 같다.

string은 클래스이기 때문에 생성자가 있다. 두 번째 문장의 경우 string s; 이렇게 선언한 것과 같으므로 string 클래스의 인자 없는 생성자가 호출된다. 따라서 일단 빈 문자열을 가진 객체가 생성된다.

그러고 나서 "Welcome to c++"라는 리터럴을 리터럴 저장소에서 복사해서 가져오는 것이다.

따라서 두 문장은 결과적으로는 같지만 내부적으로 동작되는 것은 다르다.

 

하지만 요즘은 내부적으로 optimizing(최적화) 시키기 때문에 시간 차이가 거의 없다고 한다.


문자열 추가

(참고로 UML 클래스 다이어그램에서 +는 public을, -는 private을 뜻한다.)

+append(s :string): string 문자열 s를 string 객체에 추가
+append(s: string, index: int, n: int): string 문자열 s를 index 위치에서 n개의 문자를 string에 추가
+append(s: string, n: int): string 문자열 s의 처음부터 n개의 문자를 string에 추가
+append(n: int, ch: char): string n개의 문자 ch를 string에 추가

①append(s :string)

string s1("Welcome");
s1.append(" to C++");
cout << s1 << endl; // Welcome to C++ 출력

②append(s: string, index: int, n: int)

string s2("Welcome");
s2.append(" to C and C++", 0, 5);
cout << s2 << endl; // Welcome to C 출력

여기서 헷갈리지 말아야 할 것은

s2.append(문자열, 0, 5");는 0번째 인덱스에서 시작해서 5개를 추가한다. ("0번째 인덱스부터 5번째 인덱스까지"가 아님)

 

③append(s: string, n: int)

string s3("Welcome");
s3.append(" to C and C++", 5);
cout << s3 << endl;

s3.append(문자열, 5);는 처음부터 5개를 s3에 추가하는 것이다. 즉 s3.append(문자열, 0, 5);와 같다.

 

④append(n: int, ch: char)

string s4("Welcome");
s4.append(4, 'G');
cout << s4 << endl;

이 함수에서 두 번째 인자는 char 타입이어야 한다. 'G'는 char 타입의 문자이지만 "G"는 string 클래스의 문자열이다.

작은따옴표는 char(문자), 큰 따옴표는 string(문자열)이라는 것 잊지 말자.


문자열 대입

+assign(s[]: char): string 문자 배열 또는 문자열 s를 string 객체에 대입
+assign(s: string, index: int, n: int): string 문자열 s의 index 위치에서 n개의 문자를 string 객체에 대입
+assign(s: string, n: int): string 문자열 s의 처음부터 n개의 문자를 string 객체에 대입
+assign(n: int, ch: char): string n개의 문자 ch를 string에 대입

①assign(s[]: char)

string s1("Welcome");
s1.assign("Dallas"); 
cout << s1 << endl; // Dallas 출력

②assign(s: string, index: int, n: int)

string s2("Welcome");
s2.assign("Dallas, Texas", 0, 5); 
cout << s2 << endl; // Dalla 출력

③assign(s: string, n: int)

string s3("Welcome");
s3.assign("Dallas, Texas", 5); 
cout << s3 << endl; // Dalla 출력

④assign(n: int, ch: char)

string s4("Welcome");
s4.assign(4, 'G'); 
cout << s4 << endl; // GGGG 출력

at, clear, erase, empty 함수

+at(index: int): char 문자열로부터 index 위치의 문자를 반환
+clear(): void 문자열의 모든 문자 제거
+erase(index: int, n: int): string 문자열의 index 위치에서 시작해서 n개의 문자 제거
+empty(): bool 문자열이 비어있으면 true 반환, 아니면 false 반환
string s1("Welcome");
cout << s1.at(3) << endl;         // c 출력
cout << s1.erase(2, 3) << endl ;  // Weme 출력
s1.clear();                	// s1은 빈 문자열
cout << s1.empty() << endl ;     // s1은 비어있으므로 1(true)을 출력한다.                                    

length, size, capacity 함수

+length(): int 문자열에서 문자의 개수를 반환
+size(): int length()와 같음
+capacity(): int 문자열에 할당된 저장 공간의 크기를 반환
+c_str(): char 문자열에 대한 c문자열을 반환
+data(): char c_str()과 같음
string s1("Welcome");
cout << s1.length() << endl;       // 길이는 7
cout << s1.size() <<endl;          // 크기는 7
cout << s1.capacity() << endl;     // 용량은 15

s1.erase(1, 2);                    //s1은 Wcome가 됨
cout << s1.length() << endl;       // 길이는 5
cout << s1.size() << endl;         // 크기는 5
cout << s1.capacity() << endl;     // 길이는 줄었지만 용량은 여전히 15

length()나 size()는 우리가 직접 글자 수를 세어보면 알 수 있지만 capacity()의 경우 직접 출력해보지 않으면 알 수 없다.

이 경우 운영체제가 15byte를 잡고 시작한 것이다. 규칙은 없고 OS가 여유있게 잡아놓고 나중에 필요하면 용량을 증가시킨다. 운영체제가 몇 바이트를 잡았는지는 문자열객체.capacity()로 직접 찍어봐야 알 수 있다.


문자열 비교 함수

+compare(s: string): int 문자열 객체와 s를 비교해서 정수(-1, 0, 1)를 반환한다.

두 개의 문자열 내용을 비교해야 할 때에는 compare() 함수를 사용한다.

두 개의 문자열을 비교하는데 왜 인자가 한 개일까? 

문자열 객체 자기 자신매개변수로 들어온 문자열을 비교한다.

string s1("Welcome");
string s2("Welcomg");

cout << s1.compare(s2) << endl; 	// 앞이 작으므로 -1 반환
cout << s2.compare(s1) << endl; 	// 앞이 크므로 1 반환
cout << s1.compare("Welcome") << endl; 	// 같으므로 0 반환

문자열을 비교하는 방법은 앞에서부터 한 글자씩 비교하는 것이다.

s1.compare(s2);의 경우를 예로 살펴보자.

일단 s1의 0번 인덱스에 해당하는 문자인 'W'와 s2의 0번 인덱스에 해당하는 문자 'W'를 비교한다. 같다.

그러면 s1의 1번 인덱스의 'e'와 s2의 1번 인덱스 'e'를 비교한다. 같다.

그러면 또 다음 인덱스인 2번 인덱스를 비교한다.

그렇게 하다가 6번 인덱스를 비교할 차례이다.

s1의 6번 인덱스인 'e'와 s2의 6번 인덱스인 'g'를 비교한다. 'e'의 아스키코드 값은 101, 'g'의 아스키코드 값은 103이다.

101 - 103은 음수이다. 이렇게 객체 자신(이 경우 s1)이 인자로 들어온 문자열(이 경우 s2)보다 작을 경우 -1을 반환한다.

 

s2.compare(s1); 처럼 객체 자신이 인자로 들어온 문자열보다 클 경우 1을 반환한다.

s1.compare("Welcome"); 처럼 문자열 객체 자신과 인자로 들어온 문자열이 일치하면 0을 반환한다.

 

만약 헷갈리고 외우기가 어렵다면 앞의 것에서 뒤의 것을 뺀다고 생각하면 된다.

앞의 것이 더 크면 1, 뒤의 것이 더 크면 -1, 같으면 0.


부분 문자열 구하기

+substr(index: int, n: int): string index 위치부터 n개의 문자열을 반환
+substr(index: int): string index 위치부터 끝까지의 문자열을 반환
string s1("Welcome");
cout << s1.substr(0, 1) << endl;  // W 출력(0위치부터 1개)
cout << s1.substr(5) << endl;    // me 출력(5위치부터 끝까지)
cout << s1.substr(3, 3) << endl; // com 출력(3위치부터 3개)

여기서 헷갈리지 말아야 할 것은 s1.substr(5);은 처음부터 5까지도 아니고, 처음부터 5개도 아니고 인덱스 5 위치부터 끝까지이다.

아까 위에서 append() 함수의 경우 s1.append(" to C and C++", 5);는 처음부터 5개이기 때문에 이것과 헷갈릴 수 있다.

append() 함수는 처음부터 5개, substr() 함수는 5부터 끝까지라는 것 꼭 기억하자.

또 하나 기억해야 할 것은 substr() 함수는 원본을 바꾸지 않는다는 것이다. 단지 부분문자열을 반환만 할 뿐이다.

s1.substr(0, 1)을 출력하면 W를 출력하겠지만 s1이 W로 바뀌는 것은 아니다. s1은 "Welcome" 그대로이다.


문자열 검색

+find(c: char): int 문자열에서 문자 c가 발견되는 최초 인덱스를 반환
+find(c: char, index: int): int index부터 찾아서 문자 c가 발견되는 최초 인덱스를 반환
+find(s: string): int 문자열에서 문자열 s가 발견되는 최초 인덱스를 반환
+find(s: string, index: int): int index부터 찾아서 문자열 s가 발견되는 최초 인덱스를 반환
//문자 검색
cout << s1.find('o') << endl;      // 4 출력(처음위치부터 찾음)
cout << s1.find('o', 6) << endl;   // 9 출력(인덱스 6 부터 찾음)

//문자열 검색
string s1("Welcome to HTML");
cout << s1.find("co") << endl;     // 3 출력(처음위치부터 찾음)

if (s1.find("co", 6) == string::npos)//찾지 못하면(인덱스 6부터 찾음) string::npos 반환
	cout << "co는 없습니다" << endl;

 

find() 함수는 최초 발견된 위치를 반환하기 때문에 문자열에서 'o'가 있는 모든 인덱스를 찾고 싶으면 이렇게 사용하면 된다.

string s1("Welcome to c++");
int idx = 0;
int count = 1;
while (s1.find('o', idx) != string::npos) {
	idx = s1.find('o', idx);
	cout << count << "번째로 발견된 o의 위치는 " << idx++ << "입니다." <<endl;
	count++;
}

이렇게 find() 함수의 반환값 + 1을 find 함수의 두 번째 인자로 주면 계속 검색할 수 있다. (여기에서는 후위연산자로 idx를 1 증가시켜줬다)

만약에 찾지 못하면 string::npos를 반환하는데 npos는 const형 static 변수이다. (npos는 not position의 줄인 말이다.)

static 멤버를 사용할 때에는 클래스이름::을 앞에 붙여줘야 한다.


문자열 삽입과 교체

+insert(index: int, s: string) 문자열의 index 위치에 문자열 s 삽입
+insert(index: int, n: int, char: c) 문자열의 index 위치에 문자 c를 n개 삽입
+replace(index: int, n: int, s: string) 문자열의 index 위치부터 n개를 문자열 s로 교체
string s1("Welcome to HTML");
s1.insert(11, "C++ and "); // s1의 인덱스 11 위치에 문자열 삽입
cout << s1 << endl;	// Welcome to C++ and HTML 출력

string s2("AA");
s2.insert(1, 4, 'B'); //s2의 인덱스 1 위치에 문자 4개 삽입
cout << s2 << endl; //ABBBBA 출력

string s3("Welcome to HTML");
s3.replace(11, 4, "C++"); // s3의 인덱스 11부터 4개를 문자열로 교체
cout << s3 << endl;	// Welcome to C++ 출력

문자열 연산자

[] 배열 첨자 연산자. 첨자 안의 인덱스에 해당하는 문자에 접근
= 한 문자열의 내용을 다른 문자열로 복사
+ 두 개의 문자열을 새로운 하나의 문자열로 연결
+= 하나의 문자열 내용을 다른 문자열에 추가
<< 문자열을 스트림에 삽입
>> 스트림으로부터 공백이나 NULL 문자에 의해 구분되는 문자열 추출
==, !=, <, <=, >, >= 문자열 비교를 위한 관계연산자
string s1="ABC";
string s2 = s1;
cout << s1[0];  // A 출력

string s3 = s1 + "DEFGH";  //s3는 ABCDEFGH
s1 += "ABC";  //s1은 ABCABC
cout << (s1<=s3) << endl ;  // 1(true) 출력

s1[0]은 s1.at(0)과 똑같고 s1 += "ABC"는 s1.append("ABC")와 똑같다.

+=라는 연산자를 그런 역할을 하도록 내부적으로 그렇게 정의해놓은 것이다.

그런데 +=보다는 append() 함수를 사용하는 것이 문자열을 더 정교하게 컨트롤할 수 있다.

물론 단순히 문자열을 하나의 문자열 뒤에 통째로 추가하고 싶을 때에는 +=와 append()나 똑같기 때문에 아무거나 사용해도 된다.

또 주의해야 할 것은 <= 연산자는 참일 때 1(true)를 리턴하지만 compare() 함수는 두 문자열이 같을 때 1을 리턴한다.

728x90

+ Recent posts