getline과 cin
같이 있을 때 골치아픈 두 놈, 차이점을 알아보자
PS를 하다 보면 공백 단위가 아닌,
이 문제처럼 한 줄 단위로 입력을 받아야 하는 경우가 간혹 있습니다.
이 때, C++ 스타일로 입출력을 받을 수 있는 함수가 바로 getline()
입니다.
getline()
getline 함수는 <string>
헤더에 포함된 함수로,
원형 먼저 살펴보도록 하겠습니다.
istream& getline (istream& is, string& str, char delim);
-
istream& is
: 입력을 받을 입력 스트림을 지정합니다.a. 보통 표준입출력(STDIO)를 통해 입력을 받으므로,
cin
을 넣어줍니다.b. 파일 입력 등을 사용하는 경우 파일 입력 스트림을 넣을 수도 있겠네요.
-
string& str
: 입력된 내용을 저장할 문자열 객체를 지정합니다. -
char delim
: 입력을 끝낼 문자를 지정합니다. 입력하지 않는 경우, '\n'가 기본값으로 사용됩니다.
위에서 예시로 든 문제처럼, ,
를 기준으로 입력을 받고 싶다면
string s;
getline(cin, s, ',');
이렇게 사용할 수 있습니다.
주의점
이렇게 편한 getline도 유의할 점이 있습니다.
바로, 우리가 주로 사용하는 cin
과 함께 사용하게 될 경우 입니다.
cin
과 getline
은 입력을 끝내는 기준에서도 차이가 있지만,
입력 버퍼에 남아있는 문자들을 처리하는 방식에도 차이가 있습니다.
cin
의 경우에는, delimiter로 사용되는 ' '(공백)과 '\n'(개행문자)가 입력 버퍼에 남아있으면,
delimiter가 나오지 않을 때 까지 입력 버퍼 앞쪽의 내용을 버립니다. (Trim 처리)
delimiter가 아닌 다른 문자를 입력 받고, 다시 delimiter를 만나면,
해당 delimiter는 입력 버퍼에 둔 채, 입력을 종료합니다.
getline
의 경우에는, cin
처럼 앞쪽 delimiter를 처리하는 Trim 과정이 없어,
입력 버퍼에 delimiter가 남아있다면 그대로 입력을 끝내버립니다.
또한, cin
과는 달리,
getline은 delimiter를 만나게 되면, 해당 delimiter를 입력 버퍼에서 제거합니다.
예시1
string a;
int b;
getline(cin, a);
cin >> b;
// 입력:
// Hello World!
// 3
//
// 결과:
// a: Hello World!
// b: 3
위 경우, a와 b에 어떤 입력이 들어와 있을지 생각해보면,
처음에는 입력 버퍼에는 'Hello World!\n3'가 들어있습니다.
getline
은 'Hello World!'를 a에 저장하고, delimiter인 '\n'을 버퍼에서 제거하여,
입력 버퍼에는 '3'만 남아있게 됩니다.
cin
은 입력 버퍼에 남은 3을 입력받아, b에 3을 저장하고, 입력을 종료합니다.
따라서, a에는 'Hello World!', b에는 '3'이 정상적으로 입력됩니다.
예시2
int a;
string b;
cin >> a;
getline(cin, b);
// 입력:
// 3
// Hello World!
//
// 결과:
// a: 3
// b: ""
반대로, 이 경우에는 a와 b에 어떤 입력이 들어오는지 살펴보겠습니다.
처음 입력 버퍼에는 '3\nHello World!'가 들어있습니다.
cin
은 입력 버퍼에 있는 3을 a에 저장하고, delimiter인 '\n'을 버퍼에 남겨둡니다.
따라서, 이제 입력 버퍼에는 '\nHello World!'가 들어있습니다.
getline
은 입력 버퍼 제일 앞쪽에서 delimiter인 '\n'을 입력 받았으니,
'\n'을 입력버퍼에서 제거하고, b에는 아무것도 저장하지 않은 채, 입력을 종료합니다.
따라서, a에는 3이 정상적으로 입력되지만, b에는 빈 문자열만 입력되는, 원하지 않는 상황이 생길 수 있습니다.
해결방법
많은 해결 방법이 있겠지만, 여기서는 입력 버퍼의 제일 앞에 있는 문자들을 없애는
cin.ignore()
함수를 활용한 방식을 사용하겠습니다.
int a;
string b;
cin >> a;
cin.ignore();
getline(cin, b);
// 입력:
// 3
// Hello World!
//
// 결과:
// a: 3
// b: Hello World!
이전과 동일하게 a를 입력받고 난 뒤에, 입력 버퍼에는 '\nHello World!'가 저장되어 있을겁니다.
하지만, cin.ignore()
가 호출되면 입력 버퍼 제일 앞의 '\n'를 제거해
입력 버퍼에는 'Hello World!'만 남게 되고,
이번에는 getline
함수가 정상적으로 입력을 받아 b에 'Hello World!'가 저장되게 됩니다.
cin.ignore
함수를 잘 사용하면, 입력 버퍼에서 여러 문자를 제거하거나,
특정 문자가 나올 때까지 입력 버퍼를 제거할 수 있으니, 공식 문서에서 한번 훑어보는 걸 추천드립니다.
정리
cin
은 앞쪽 delimiter들을 무시하지만, 입력 버퍼에 delimiter를 남겨둔다.
getline
은 앞쪽 delimiter를 무시하지 못하지만, 입력 버퍼에 delimiter를 남기지 않는다.
cin.ignore()
은 입력 버퍼 앞의 문자들을 지운다.