c++에서는 문자열 관련된 라이브러리가 cstring 그리고 string 두 종류가 있다.
null문자로 끝나는 char* 형식을 따르는 C언어 방식의 문자열 라이브러리(cstring)와 std::string을 따르는 라이브러리(string) 이렇게 두 개요! 문자열을 처리하는 방법이 두 가지가 되면서 그 방법에 따라 getline()함수도 두 종류가 존재하게 된겁니다. 즉 각 getline()함수는 문자열을 처리하는 방식이 달라요.
istream 라이브러리에 속해있는 getline()함수는 뒤에 '/0'이 붙는 char* 형식 즉 클래직한 C언어 문자열을 따르는 입력 방법입니다. 반면에 string 라이브러리에 속해있는 getline()함수는 std::string 방식으로 동작해요 ㅎㅎ 그래서 인자를 보면 istream:getline()함수의 경우 char* 타입을 인자로 받는데 string의 getline()함수는 string을 인자로 받을 수 있습니다.
각가의 문법을 좀 더 자세히 알아볼게요.
1.std::istream::getline - cin.getline()
istream& getline(char* s, streamsize n);
istream& getline(char* s, streamsize n, char delim);
인자:
s- C 형식 문자열을 저장할 배열을 가리키는 포인터
n- 저장할 문자의 최대 개수 (끝의 종료 널 문자를 포함한 값). 만약 입력 스트림의 최대 크기에 도달하여 입력이 중단되면failbit플래그가 설정됩니다.
delim- 제한자로 이 문자에 도달시 추출이 중단됩니다. 이 때 이 문자는 s에 기록되지는 않지만 스트림에서 사라지게 됩니다.
cin.getline(greeting, 10)은 10자를 입력받아 greeting이라는 변수에 넣어라! 라는 뜻입니다.
그런데 greeting에 저장되어 있는 것은 10자인 'hello Cons'이 아닌 'hello Con'이네요. getline()함수는 c스타일 문자열 형식을 따른다고 했죠?! 마지막에 널('\0)을 저장해줘야 하기 때문에 실질적으로 저장되는 자리수는 n-1인 9자리입니다. 또 n의 자리수를 크게 하면 파일에 쓰여진 글은 두 줄이지만 한 줄(hello File!)까지만 출력됩니다.
당연히 문자열에 C-배열 타입 말고 string을 인자로 넣으면 에러뜹니다. string을 인자로 넣고 싶으면 string 라이브러리의 getline()을 사용하기!
istream& getline(char* s, streamsize n);
즉 이 함수는n-1개의 문자 개수만큼 읽어와서 s에다가 저장시킵니다. 3번째 인자인 char delim을 별도로 지정해주지 않으면엔터('\n')로 인식하기 때문에 n이 클 경우 s에는 한 줄이 저장돼요. 3번째 인자를 지정해주면 그 문자를 만나면 읽어오지 않기 때문에그 제한자(delim) 문자직전까지 읽어다가 s에 저장해줍니다. 즉 ifs.getline(greeting, 10, 'i');하면 i직전인 hello F까지 저장됩니다.
2. std::getline (string)
istream& getline (istream& is, string& str);
istream& getline (istream& is, string& str, char delim);
인자:
is- 입력스트림 오브젝트 ex) cin
str- 입력받은 문자열을 저장할 string 객체
delim - 제한자로 이 문자에 도달시 추출이 중단됩니다. 이 때 이 문자는 s에 기록되지는 않지만 스트림에서 사라지게 됩니다.
# 기본 사용법
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
string greeting;
getline(cin, greeting);
cout << "console 입력 값:"<<greeting <<"\n";
ifstream ifs;
ifs.open("fileinput.txt");
getline(ifs, greeting);
cout << "file 입력 값:" << greeting << "\n";
return 0;
}
결과는 위와 똑같아요 ㅎㅎ 대신 이 함수는 입력스트림이 첫 번째 인자로 오고 두 번째 인자로 string 객체를 받습니다. string으로 문자열을 해석하는 함수이기 때문에 C스타일의 문자열 (char *)을 인자로 넣어주면 안돼요!
3. 주의해야 할 점(getline 함수 둘 다 모두 해당)
#include <iostream>
using namespace std;
int main()
{
char a[100], b[100], c[100];
cin >> a;
cin.getline(b, 100);
cin.getline(c, 100);
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "c:" << c << endl;
return 0;
}
이렇게 코드를 작성한 후 입력을 아래처럼 주었을 경우 결과가 어떻게 될까요?
입력: hello 엔터 Ella 엔터 Yang
a에 hello, b에 Ella c에 Yang이 들어갔을까요?
출력은 이처럼 됩니다. 조심해야 해요. 이럴 경우, cin 이후에버퍼를 비워주는 함수(ex cin.ignore())를 실행해서 버퍼를 비워주고 난 뒤 getline으로 입력을 받아야 해요.
이런 문제는 cin함수와 getline함수의 차이에서 오는데요
어떤 원리로 입력을 받아들이는지 하나씩 살펴보면서 설명할게요
-- cin의 경우
#include <iostream>
using namespace std;
int main()
{
char a[100], b[100], c[100];
cin >> a;
cin >> b;
cin >> c;
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "c:" << c << endl;
return 0;
}
입력을 'hello Ella Yang'으로 사이사이에 공백을 충분히 주고 넣어줬어요.
이 결과에서 알 수 있는 것은 cin은 공백을 무시한다는거예요.cin은 입력값의 기준을 띄어쓰기, 엔터, 탭 등으로 나눕니다.그래서 문자를 읽다가 띄어쓰기를 만나면 그 전까지를 하나의 입력으로 받아들여요.반면 띄어쓰기, 엔터, 탭 등 이런 문자가 먼저 나오면 그냥 무시합니다.대신에 버퍼에는 그대로 남아있어요.
cin이 버퍼에 있는 글자 하나하나를 읽어들입니다. hello까지 읽고 공백이 나오니까 공백 전 hello까지를 a에 저장시켜요.
그러면다음 버퍼에 남아있는 문자들은 띄어쓰기들이겠죠!! 이때 cin은 멀쩡한 문자가 나올 때까지 공백들을 다 무시합니다. 그리고 Ella가 나오면 읽고 저장합니다. 이런식으로 동작해요.
입력함수가 계속 cin이라면 문제되지 않아요.cin은 내부적으로 공백 같은 문자들을 무시해주는 formatting된 함수이기 때문에 버퍼에 공백이 남아도 무시하고 지나가기 때문이죠.
-- getline() 의 경우
getline이 하나의 입력으로 받아들이는 기준은 따로 delim을 지정해주지 않는한 엔터('\n')입니다.
그런데 getline()함수는unformatting 함수입니다.
getline은엔터('\n')를 무시하지 않고 잡습니다!이 차이에서 문제가 발생해요.
getline만 사용할 경우에는 문자열을 치고 엔터를 쳤을 때, 엔터 직전까지를 하나의 입력으로 받아들이고엔터를 버려요!버퍼에 남기지 않아요. 그래서 다음 getline을 사용할 때에도 버퍼에 남아있는 것이 없기 때문에 문제가 되지 않습니다.
하지만 앞에서 봤듯이 cin은 엔터를 버리지 않고 버퍼에 남긴다고 했죠? 그냥 cin은 엔터를 무시하는 기능이 있기 때문에 문제되지 않았던 거예요. getline은 무시하는 기능이 없고 잡아서 읽기 때문에 반드시 두 함수에서 나오는 차이점을 숙지하고 사용해야 한다.