Study/C++

[C++]04.2 복합데이터형 - 문자열

SigmoidFunction 2023. 3. 9. 16:28
728x90

문자열이란 메모리에 바이트 단위로 연속적으로 저장되어 있는 문자들을 말한다. 

 

문자열이 너무 길 경우 한행으로 표현할 수 없을 때는 큰따옴표로 묶인 두 문자열을 하나로 결합할 수 있다.

빈칸, 탭, 캐리지리턴과 같은 화이스스페이스로 분리된 두개의 문자열 상수는 하나의 문자열 상수로 결합된다. 

아래의 세개의 출력문은 모두 동등하다.

 

cout << "I'd give my right arm to be" " a great
		violinist.\n";
cout << "I'd give my right arm to be a great violinist.\n";
cout << "I'd give my right ar"
      "m to be a great violinist.\n";

 

 

 

아래 예제를 보자.

// string.cpp -- 배열에 문자열을 저장한다.
#include <iostream>
#include <cstring>      // strlen() 함수를 사용하기 위해
int main()
{
    using namespace std;
    const int Size = 15;
    char name1[Size];               // 비어있는 배열
    char name2[Size] = "C++owboy";  // 문자열 상수로 초기화된 배열
    // 참고: 어떤 C++에서는 name2 배열을 초기화하기 위해서
    // static 키워드를 사용해야 한다.

    cout << "Hello! I'm " << name2;
    cout << "! What's your name?\n";
    cin >> name1;
    cout << "Ah, " << name1 << "! Your name length is ";
    cout << strlen(name1) << ".. but \n";
    cout << sizeof(name1) << "bytes array stored!\n";
    cout << "Your name starts with " << name1[0] << "\n";
    name2[3] = '\0';        // null char
    cout << "The first threee characters ofo my name are as follows: ";
    cout << name2 << endl;

    return 0;
}


// Hello! I'm C++owboy! What's your name?
// Andrew
// Ah, Andrew! Your name length is 6.. but 
// 15bytes array stored!
// Your name starts with A
// The first threee characters ofo my name are as follows: C++

 

C++owboy에서 3번 인덱스를 널값으로 변환했더니 name2를 출력하면 2번인덱스까지만 출력되었다.

 

문자열은 널값이 나오면 출력을 멈춘다.

 

strlen은 배열의 전체 크기가 아니라 널값을 제외한 문자열의 크기만 리턴한다. 

 

Andrew라면 배열의 최소 크기는 7이다. 

 

 

https://simplesnippets.tech/cpp-strings-with-explanation-example/

 

 

 

 

아래 코드는 문자열 입력의 결함을 나타냄

// instr1.cpp -- 여러개의 문자열을 읽는다.
#include <iostream>
int main()
{
    using namespace std;
    const int ArSize = 20;
    char name[ArSize];
    char dessert[ArSize];

    cout << "Input your name:\n";
    cin >> name;
    cout << "Input your favorite dessert:\n";
    cin >> dessert;
    cout << "I'll prepare delicious " << dessert;
    cout << ". Thank you. " << name << " sir!\n";
    return 0;
}

// Input your name:
// Mike Pancake
// Input your favorite dessert:
// I'll prepare delicious Pancake. Thank you. Mike sir!

cin이 문자열의 끝을 인식하는 방법 때문이다. 키보드로는 끝내기 널 문자를 입력할 수 없기 때문에 cin에게 문자열의 끝을 알려주는 다른 수단이 필요하다. 

 

cin은 빈칸, 탭, 캐리지 리턴과 같은 화이트스페이스가 있으면 그 위치에서 문자열이 끝난 것으로 간주한다.

 

그래서 Mike Pancake에서 Mike를 name에 넣고 빈칸이 있으니 Pancake를 다음 dessert에 넣은 것이다.

 

 

 

한 줄을 모두 읽고 싶다면 getline()과 get()을 사용하면 된다. 

getline()은 개행문자를 읽어서 폐기하는 반면에 get()은 입력큐에 개행 문자를 남겨둔다.

getline()은 Enter키로 전달되는 개행문자를 입력의 끝으로 간주하여 한행 전체를 읽는다!

 

20개의 원소를 가진 name배열에 저장하고 싶다면

cin.getline(name, 20);

 

// instr2.cpp -- getline() 함수로 한 행을 읽는다.
#include <iostream>
int main()
{
    using namespace std;
    const int ArSize = 20;
    char name[ArSize];
    char dessert[ArSize];

    cout << "Input your name:\n";
    cin.getline(name, ArSize);      // 개행 문자가 있는 곳까지 읽는다.
    cout << "Input your favorite dessert:\n";
    cin.getline(dessert, ArSize);
    cout << "I'll prepare delicious " << dessert;
    cout << ". Thank you. " << name << " sir!\n";
    return 0;
}


// Input your name:
// Dirk Hammernose
// Input your favorite dessert:
// Radish Torte
// I'll prepare delicious Radish Torte. Thank you. Dirk Hammernose sir!

 

띄어쓰기도 모두 인식한 모습!

 

get()은 개행문자를 읽어서 버리지 않고 그대로 남겨두기 때문에 연달아 2번 호출하면 문제가 발생한다.

그러나 다음과 같이 사용할 수는 있다.

cin.get(name, ArSize);
cin.get()
cin.get(dessert, ArSize);

OR

cin.get(name, ArSize).get();

cin.get(name, ArSize);가 cin객체를 리턴하여 뒤에 결합된 get()함수를 호출하는 객체로 사용된다.

 

비슷하게 

cin.getline(name, ArSize).getline(dessert, ArSize);

getline을 두번 호출하는 것과 같다. 각각 name, dessert에 저장한다. 

 

 

get() 사용 예제를 살펴보자

// instr3.cpp -- get() & get()으로 여러 단어를 읽는다.
#include <iostream>
int main()
{
    using namespace std;
    const int ArSize = 20;
    char name[ArSize];
    char dessert[ArSize];

    cout << "Input your name:\n";
    cin.get(name, ArSize).get();        // 문자열, 개행문자를 읽는다.
    cout << "Input your favorite dessert:\n";
    cin.get(dessert, ArSize).get();
    cout << "I'll prepare delicious " << dessert;
    cout << ". Thank you. " << name << " sir!\n";
    return 0;
}

// Input your name:
// Mai Parfait
// Input your favorite dessert:
// Chocolate Mousse 
// I'll prepare delicious Chocolate Mousse. Thank you. Mai Parfait sir!

 

getline()은 사용하기가 좀 더 편하고 get()은 에러 체크하는 것이 더 쉽다.

 

 

 

 

 

문자열과 수치의 혼합입력은 문제가 발생할 수 있다.

// numstr.cpp -- 수치 입력 뒤에 오는 문자열 입력
#include <iostream>
int main()
{   using namespace std;
    cout << "When did you move into your current apartment?\n";
    int year;
    cin >> year;
    cout << "Could you tell me your city?\n";
    char address[80];
    cin.getline(address, 80);
    cout << "Apartment occupancy year: " << year << endl;
    cout << "City: " << address << endl;
    cout << "Registration completed!\n";
    return 0;
}


// When did you move into your current apartment?
// 1966
// Could you tell me your city?
// Apartment occupancy year: 1966
// City:
// Registration completed!

주소를 입력할 기회를 안준다. year를 읽고 Enter키가 만들어낸 개행 문자를 입력 큐에 남겨두기 때문에 발생하는 데

입력 큐에 남겨진 개행 문자를 빈행으로 읽어들여 address배열에 널 문자열을 대입한다. 

 

이 문제를 해결하는 방법은 주소를 읽기 전에 개행 문자를 읽어 허공에다가 버리는 것이다.

 

또는 매개변수를 사용하지 않는  get()이나 하나의 char형 매개변수를 사용하는  get()을 호출하는 것을 포함하여 여러가지 방법으로 해결할 수 있다.

// numstr2 -- 숫자입력 후 남은 개행문자로 인한 입력 오류 처리방법
#include <iostream>
int main()
{
    using namespace std;
    cout << "When did you move into your current apartment?\n";
    int year;
    cin >> year;
    cin.get();
    // (cin >> year).get(); 또는 (cin >> year).get(ch);
    // 두가지 방법으로 처리가 가능하다.

    cout << "Could you tell me your city?\n";
    char address[80];
    cin.getline(address, 80);
    cout << "Apartment occupancy year: " << year << endl;
    cout << "City: " << address << endl;
    cout << "Registration completed!\n";
    return 0;
}

// When did you move into your current apartment?
// 2023
// Could you tell me your city?
// Anyang
// Apartment occupancy year: 2023
// City: Anyang
// Registration completed!

 

 

C++은 문자열 처리를 위해 배열 대신 포인터를 더 많이 사용한다!

728x90