이번엔 문자열(string)을 다루는 클래스인 string 클래스를 파헤쳐 보겠습니다.
이번에는 std::string 의 모든 것을 정리해봤으니 끝까지 읽어 주세요.
string 입/출력 방법
이전에 C에서는 char* 나 char[]의 형태로 문자열을 다뤘다면, c++에서는 문자열을 하나의 변수 type으로 간주하여
다룰 수 있게 합니다.
char*나 char[]와 다르게 문자열 끝에 '\0'이 포함되지 않아 문자열의 길이를 동적으로 변경이 가능합니다.
지난번에 말씀드린 것과 같이 cin을 통해 string을 입력받을 수 있었습니다.
#include <iostream>
#include <string>
int main()
{
std::string str;
std::cout << "cin 입력:" << std::endl;
std::cin >> str;
std::cout << "cin 결과:" << str << std::endl;
return 0;
}
cin>>str : 공백(space)가 입력될 때까지 값을 받습니다.
그래서 cin에 Console Input 을 입력하면 str 에는 Console 밖에 저장되지 않습니다.
cin 의 공백이 들어오면 더이상 받을 수 없는 단점을 보완하려면 getline()을 사용하면 됩니다.
#include <iostream>
#include <string>
int main()
{
std::string str;
std::cout << "getline 입력:";
std::getline(std::cin, str);
std::cout << "getline() 결과:"<<str << '\n';
std::cout << "getline 입력:";
std::getline(std::cin, str, 'i');
std::cout << "getline() 결과:" << str << std::endl;
return 0;
}
getline()은 두개가 있는데, #include <fstream>을 추가하고
std::cin.getline()으로 얻어오는 방법도 있지만 #include <string>을 추가하면 std::getline()을 사용할 수 있습니다.
getline은 아래와 같이 구성되어 있습니다.
위 소스 코드의 결과는 아래와 같습니다.
getline() 에서 구분 문자에 'i'를 넣으니까 i 전까지인 getl 만 읽어 옵니다.
구분 문자는 ',' 등 다른 기호를 사용하실 수도 있습니다.
string 객체 생성 방법
string 객체를 생성하는 방법은 여러가지가 있지만 대표적으로 아래와 같습니다.
// 빈 문자열 생성
std::string str;
// "abcdefg"로 선언된 string 생성
std::string str1 = "abcdefg";
std::string str2;
str2 = "abcdefg";
std::string str3("abcdefg");
// str3 문자열을 복사한 str4 생성
std::string str4(str3);
// C에서의 문자열과 호환 가능
char s[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' };
std::string str5(s);
// new를 이용한 동적 할당
std::string *str6 = new std::string("abcdefg");
string 클래스 멤버 함수
std::string은 STL 중 하나입니다.
그럼 vector와 같이 유사한 멤버 함수들을 가지고 있겠죠? 그걸 알아보겠습니다.
str.at(idx)
|
idx 위치 문자 반환, 범위 유효성 체크 O
|
str[idx]
|
idx 위치 문자 반환, 범위 유효성 체크 X
|
str.front()
|
문자열의 가장 앞의 문자 반환
|
str.back()
|
문자열의 가장 뒤의 문자 반환
|
#include <iostream>
#include <string>
int main()
{
std::string str = "abcdefg";
std::cout << "4번째 원소(d):" << str.at(3) << std::endl;
std::cout << "6번째 원소(f):" << str[5] << std::endl;
std::cout << "제일 앞 원소(a):" << str.front() << std::endl;
std::cout << "제일 뒤 원소(g):" << str.back() << std::endl;
return 0;
}
index는 항상 '0'부터 시작한다는 사실!!
그래서 idx 자리에 3이 있지만 4번째 원소를 반환하죠.
2. 문자열의 크기
str.length()
|
문자열 길이 반환
|
str.size()
|
문자열 길이 반환(length()와 동일)
|
str.max_size()
|
최대한 메모리 할당할 경우 저장할 수 있는 문자열 길이 반환
|
str.capacity()
|
문자열의 메모리 크기 반환
|
str.resize(n)
|
str을 n의 크기로 만듦. 삭제 또는 빈 공간으로 채움
|
str.resize(n, 'a')
|
n이 str 길이보다 크면 빈 공간을 'a'로 채움
|
str.shrink_to_fit()
|
capacity가 실제 사용하는 메모리보다 큰 경우 메모리 줄여 줌(메모리 낭비 제거)
|
str.reserve(n)
|
사이즈 n 만큼의 메모리 미리 할당
|
str.empty()
|
str이 빈 문자열인지 확인
|
#include <iostream>
#include <string>
int main()
{
std::string str = "abcdefg";
std::cout << "문자열 길이:" << str.length() << std::endl;
std::cout << "문자열 사이즈:" << str.size() << std::endl;
std::cout << "문자열 메모리 크기:" << str.capacity() << std::endl;
str.resize(4);
std::cout << "문자열 길이 줄임:" << str << std::endl;
str.resize(10, 'a');
std::cout << "문자열 길이 늘이고 'a'로 채움:" << str << std::endl;
str.reserve(15);
std::cout << "문자열 메모리 할당:" << str.capacity() << std::endl;
std::cout << "문자열 사이즈:" << str.size() << std::endl;
str.shrink_to_fit();
std::cout << "문자열 메모리 최적화:" << str.capacity() << std::endl;
std::cout << "문자열 비었는지 확인:" << str.empty() << std::endl;
return 0;
}
결과는 아래와 같이 나옵니다.
str.reserve(15)를 하면 메모리를 15개를 잡지만, 문자열 사이즈는 여전히 10인것을 볼 수 있습니다.
이것이 capacity와 length()/size()의 차이입니다.
3. 문자열 삽입/추가/삭제
string도 vector 처럼 사용법이 비슷하죠? string 도 STL이기 때문입니다.
str.append(str2)
|
str 뒤에 str2 문자열을 이어 붙여 줌(str + str2 와 같음)
|
str.append(str2, n ,m)
|
str 뒤에 'str2의 n index 부터 m개의 문자'를 이어 붙여 줌
|
str.append(n, 'a')
|
str 뒤에 n 개의 'a'를 붙여 줌
|
str.insert(n, str2)
|
n번째 index 앞에 str2 문자열을 삽입함
|
str.replace(n, k, str2)
|
n번째 index 부터 k개의 문자열을 str2로 대체함
|
str.clear()
|
저장된 문자열을 모두 지움
|
str.erase()
|
clear()와 같음
|
str.erase(n, m)
|
n번째 index부터 m개의 문자를 지움
|
str.erase(n, m) ← iterator
|
n~m index 문자열을 지움(n, m은 iterator임)
|
str.push_back(c)
|
str의 맨 뒤에 c를 붙여 줌
|
str.pop_back()
|
str의 맨 뒤의 문자를 제거
|
str.assign(str2)
|
str 에 str2 문자열을 할당함
|
테스트 코드는 아래와 같아요.
#include <iostream>
#include <string>
int main()
{
std::string str = "abcdefg";
std::string str2 = "hijklmn";
str.append(str2);
std::cout << "1. "<< str << std::endl;
str.append(str2, 1, 3);
std::cout << "2. " << str << std::endl;
str.append(3, 'z');
std::cout << "3. " << str << std::endl;
str.clear();
std::cout << "4. " << str << std::endl;
str.assign("abcdefg");
std::cout << "5. " << str << std::endl;
str.insert(2, str2);
std::cout << "6. " << str << std::endl;
str.replace(3, 3, "vv");
std::cout << "7. " << str << std::endl;
str.erase(0, 3);
std::cout << "8. " << str << std::endl;
str.erase();
std::cout << "9. " << str << std::endl;
str.push_back('abc');
std::cout << "10. " << str << std::endl;
str.push_back('d');
std::cout << "11. " << str << std::endl;
str.pop_back();
std::cout << "12. " << str << std::endl;
return 0;
}
결과는 아래와 같은데, 7.번에서 3번째 index 에서 3글자를 "vv"로 바꿔라 라고 하니까 3글자가 지워지고 2글자만 삽입이 되었네요.
또한 10. 번에서 push_back()에 여러글자를 넣으니까 마지막 한글자만 추가가 되네요.
4. 부분 문자/비교/복사/찾기
str.substr()
|
str 전체를 반환
|
str.substr(n)
|
str의 n번째 index부터 끝까지 부분 문자열 반환
|
str.substr(n, k)
|
str의 n번째 index부터 k개의 부분 문자열 반환
|
str.compare(str2)
|
str과 str2가 같은지 비교, str<str2인 경우 음수, str>str2인 경우 양수 반환
|
str.copy(str2, k, n)
|
str의 n번째 index부터 k개의 문자열 복사
|
str.find("abcd")
|
"abcd"가 str에 포함되어 있는지 확인, 찾으면 해당 부분 첫 index 반환
|
str.find("abcd", n)
|
n번째 index부터 "abcd"를 찾음
|
str.find_first_of("/")
|
"/"가 처음 나타나는 index
|
str.find_last_of("/")
|
"/"가 마지막으로 나타나는 index
|
#include <iostream>
#include <string>
int main()
{
std::string str = "abcdefg";
std::string str2 = "hijklmn";
std::string str3 = str.substr();
std::cout << "전체 복사:" << str3 << std::endl;
std::string str4 = str.substr(3);
std::cout << "4번째부터:" << str4 << std::endl;
std::string str5 = str.substr(2, 4);
std::cout << "3번째부터 4개 문자열:" << str5 << std::endl;
std::cout << "두 문자열 같은지:" << str.compare(str2) << std::endl;
std::cout << "문자열 찾기:" << str.find("bcd") << std::endl;
if (str.find("bcd", 2) != std::string::npos) {
std::cout << "3번째부터 문자열 찾기:" << str.find("bcd", 2) << std::endl;
}
else
std::cout << "3번째부터 문자열 찾기:" << "못찾음" << std::endl;
std::string str6 = "C:/program/temp/test";
std::cout << "'/'시작 인덱스:" << str6.find_first_of("/") << std::endl;
std::cout << "'/'마지막 인덱스:" << str6.find_last_of("/") << std::endl;
return 0;
}
실행 결과는 아래와 같아요.
string의 인자로는 인덱스와 길이를 쌍으로 입력받는 경우가 많습니다.
인덱스와 인덱스를 받는 경우가 없으니 주의하세요!
5. 기타 유용한 함수들
str.c_str()
|
string을 c스타일의 문자열로 변경
|
str.begin()
|
string의 시작 iterator 반환
|
str.end()
|
string의 끝 iterator 반환
|
swap(str, str2)
|
str과 str2를 바꿔줌
|
str = str2 + str3
|
str2와 str3를 붙여서 str에 복사함
|
str += str2
|
str 뒤에 str2를 붙여줌
|
str = str2
|
str에 str2 복사 (Deep Copy)
|
str == str2
|
str과 str2가 같은지 확인
|
str > str2, str < str2
|
str이 str2보다 사전순으로 앞인지 뒤인지 확인
|
isdigit(c)
|
#include <cctype>, c가 숫자인지 확인, 숫자이면 0이 아닌 숫자 반환
|
isalpha(c)
|
#include <cctype>, 알파벳 확인, 대문자는 1 반환, 소문자는 2 반환, 알파벳이 아니면 0 반환
|
toupper(c)
|
#include <cctype>, c를 대문자로 변환
|
tolower(c)
|
#include <cctype>, c를 소문자로 변환
|
stoi(), stof(), stol(), stod()
|
문자열을 숫자로 변환(int, float, long, double)
|
to_string(n)
|
숫자 n을 문자열로 변환
|
#include <iostream>
#include <string>
#include <cctype>
int main()
{
std::string str = "abcdefg";
std::string str2 = "hijklmn";
std::string str3 = str;
const char* carrName = str.c_str();
std::cout << "c스타일:" << carrName << std::endl;
std::string str4 = { str.begin(), str.end() };
std::cout << "문자열 bein(), end()로 복사:" << str4 << std::endl;
std::cout << "첫번째 원소값:" << *str.begin() << std::endl;
std::cout << "iterator로 전체 원소 print하기:";
for (std::string::iterator iter = str.begin(); iter != str.end(); ++iter) {
std::cout << *iter;
}
std::cout << std::endl;
std::string str5 = str + str2;
std::cout << "str + str2:" << str5 << std::endl;
str += str2;
std::cout << "str += str2:" << str << std::endl;
str = str3;
std::cout << "str = str3:" << str << std::endl;
std::cout << "str == str2:" << (str == str2) << std::endl;
std::string str6 = "123a";
for (const auto& s : str6) {
std::cout << s << " is digit?:" << std::isdigit(s) << std::endl;
}
std::string str7 = "1Ab3";
for (int i = 0; i < str7.size(); ++i) {
std::cout << str7[i] << " is alphabet?:" << std::isalpha(str7[i]) << std::endl;
}
std::string str8 = "aBcDeF";
std::cout << str8 << "를 대문자로:";
for (int i = 0; i < str8.size(); ++i) {
str8[i] = std::toupper(str8[i]);
}
std::cout << str8 << std::endl;
std::cout << str8 << "를 소문자로:";
for (auto& s : str8) {
s = std::tolower(s);
}
std::cout << str8 << std::endl;
std::string str9 = "123";
std::cout << "숫자로 변환:" << std::stoi(str9) << std::endl;
float fNum = 10.234;
std::string str10 = std::to_string(fNum);
std::cout << "to_string:" << str10 << std::endl;
return 0;
}
실행 결과는 아래와 같습니다.
여기에서 toupper(), tolower()는 대/소문자의 아스키 코드를 반환합니다.
예를 들어
#include <iostream>
#include <string>
#include <cctype>
int main()
{
std::string str = "aBcD";
for (int i = 0; i < str.size(); ++i)
std::cout << "'"<<str[i]<<"' 의 대문자 Ascii Code:" << std::toupper(str[i]) << std::endl;
return 0;
}
이렇게 해 보면,
아래와 같이 나옵니다.
그걸 std::string으로 받으면 형변환이 되어 string으로 저장되는 것 입니다.
그리고 stoi()종류나 to_string() 많이 사용하니까 꼭 알아두세요.
그리고 string to char*, char* to string 변환에 대해 궁금하시면 아래를 참조하세요