[C++] 구분자까지 문자열을 입력받는 std::getline 함수

반응형

std::getline 함수 사용법

std::getline 함수는 입력 스트림( input stream )으로부터 구분자( delimiter )까지의 문자열을 입력받는 함수입니다.
만약, 구분자를 지정하지 않은 경우, 개행 문자( '\n' )가 기본 구분자가 됩니다.

먼저, 이 함수를 사용하려면 다음의 헤더를 포함해야 합니다.

#include <string>

함수의 정의는 다음과 같습니다.

istream& getline( istream& is, string& str);
istream& getline( istream& is, string& str, char deli );

// C++ 11 이후
istream& getline( istream&& is, string& str);
istream& getline( istream&& is, string& str, char deli );

이 정의에서, 매개 변수 is는 아래 이미지의 C++ 입력스트림 istream을 가리킵니다.

 

C++의 표준 입출력 상속 관계
C++의 표준 입출력 상속 관계 ( 출처: cplusplus.com )

위에서 보면 알 수 있듯이, getline 함수는 이 istream 클래스를 상속받은 사용자 입력( std::cin ), 파일( file ), 그리고 std::string 스트림 객체로부터 데이터를 입력받을 수 있습니다.

 

그리고, 마지막 매개 변수인 deli는 입력 문자열을 분할하는 구분자입니다.
이 함수는 입력 스트림의 현 위치부터, 이 구분자까지의 문자열을 구하여, str에 그 값을 전달합니다.

 

아래 예문은 getline의 기본적인 사용법입니다.

#include <iostream>
#include <string>
using namespace std;

int main(){

  string str1, str2, str3;
  
  getline( cin, str1, ',');
  getline( cin, str2, ',');
  getline( cin, str3);

  cout << str1 << " , " << str2 << " , " << str3;
}

▲입력

apple,banana,strawberry

▼출력

apple , banana , strawberry

위의 마지막 getlne 함수 호출에서는 구분자를 지정하지 않았으므로, 개행 문자 '\n'가 구분자( delimeter )가 됩니다.

따라서, 사용자가 enter를 입력하는 순간, 입력이 끝나고 결과가 출력됩니다.

 

다음 예문은 std::cin 객체의 >> 연산자와 getline 함수를 같이 사용하는 예문입니다.

int main ()
{
  string name1, name2, name3;

  cout << "Please, enter names: \n";

  cin >> name1;

  getline (cin, name2);	// 개행문자가 구분자
  getline (cin, name3);
  
  cout << name1 << "|,|" << name2 << "|,|" << name3;
}

▼출력

Please, enter names: 
1
2
output: 1|,||,|2

위에서도 첫 번째 예문처럼, 세 번의 입력을 기대했는데, 12를 입력하자, 바로 결과가 출력되었습니다.
왜 이런 걸까요?

 

이것은, std::cin 객체의 >> 연산자가 공백, 탭, 개행 문자의 전까지만 입력을 받고 리턴하기 때문입니다.

문자 1 뒤에 개행 문자가 있기 때문에, name1에는 1이 입력되고, 입력 스트림에는 아직 개행 문자가 남아 있습니다.

 

이 개행 문자는 getline 함수를 통해서 두 번째 입력을 받을 때, 구분자로 역할을 하므로, name2는 아무런 문자도 입력받지 못했습니다.
그리고, 세 번째 입력 시, name32가 입력되어서 위의 결과가 나오게 된 것입니다.

이 결과를 수정하려면, 첫 번째 문자열 1을 입력받은 후, 입력 스트림에 아직 남아있는 개행 문자 '\n'를 제거하면 될 것입니다.
이때 사용하는 함수가 istream::ignore입니다.

istream& ignore( streamsize n = 1, int delim = EOF );

이 함수는 입력 스트림에 남아있는 데이터를 n개만큼 삭제합니다.

만약, delim 구분자를 만나면 함수는 종료합니다.

 

여기서는, 개행 문자만 제거하면 되므로, 다음 문장으로 문제는 해결됩니다.

cin.ignore();

▼출력

Please, enter names: 
1
2
3
output: 1|,|2|,|3

 

참고로, istream 스트림에 남아있는 모든 문자를 무시하려면, 아래와 같이 하면 됩니다.

#include <limits>	// for std::numeric_limits

std::cin.ignore( std::numeric_limits<std::streamsize>::max() );

이때, max()의 값을 인자로 ignore 함수에 전달하면, 이 함수는 이것을 매우 큰 숫자를 말하는 것이 아니라, 제한이 없다는 뜻으로 받아들입니다.
그 결과, 아직 입력 스트림에 남아 있는 모든 문자를 무시(제거)하게 됩니다.

 

다음 예문은, 콤마로 구분된 문자열로부터 숫자의 합을 구하는 예문입니다.

#include <iostream>
#include <string>
#include <sstream>  // for istringstream
using namespace std;

int main(){

    string str = "10, 2, 3, 5, 7, 23";

    istringstream input(str);   // std::string으로부터 입력 스트림 생성
    string val;

    int sum = 0;
    while( getline( input, val, ',') ){	// ??
    
        sum += stoi(val);	// string을 숫자로 변환
    }

    cout << "sum: " << sum;
}

▼출력

sum: 50

위에서 getline 함수가 반환하는 것은 istream 객체입니다.
그런데, 어떻게 위와 같이 while 구문을 사용할 수 있는 걸까요?

 

이것은 컴파일러에 의해서 암시적인 타입 변환이 일어나기 때문입니다.
while 구문은 괄호 안 표현식의 bool 값을 평가하는데, 이 값을 얻기 위해서 컴파일러는 istream 객체를 bool 타입의 값으로의 변환을 시도하고, 그 과정에서 bool istream::operator() 연산자를 호출하게 됩니다.

 

이 연산자는 istream의 내부 상태 플러그에 따라, truefalse를 반환하는데, 스트림이 EOF( end of file )에 도달하거나 스트림의 데이터를 읽으면서 오류가 발생하게 되면, 이 상태 플러그가 오류 상태가 되어 false를 반환하게 됩니다.
그래서, 문자열을 읽는 과정이 입력스트림의 끝에 도달하면, while 루프를 벗어나게 되는 것입니다.

 

 

 

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유