Menu

C++ 입력과 출력: cin, cout, getline, 그리고 스트림

C++에서 콘솔 I/O가 동작하는 방식: cout로 출력하기, cin으로 읽기, cin 다음의 getline에서 생기는 고전적인 개행 버그, 그리고 입력이 실패했을 때 회복하는 법.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

C++는 콘솔과 어떻게 대화하는가

C++는 <iostream> 헤더의 스트림을 통해 콘솔 I/O를 합니다. cout(문자 출력)으로 바깥세상에 쓰고, cin(문자 입력)으로 사용자로부터 읽습니다. 이들은 여러분이 이미 비트 시프트로 본 적 있는 두 연산자를, 여기서는 용도를 바꿔 사용합니다.

  • <<삽입 연산자입니다. 데이터를 cout 안으로 보냅니다.
  • >>추출 연산자입니다. 데이터를 cin 바깥으로 끌어내 변수에 담습니다.

방향을 기억하는 편리한 방법: 화살표는 데이터가 흐르는 방향을 가리킵니다. 이제 텍스트를 문자열에 담을 수 있으니, 그 텍스트를 프로그램 안팎으로 옮겨 봅시다.

cout로 출력하기

cout은 여러분이 삽입하는 모든 것을 표준 출력으로 보냅니다. 한 문장에서 여러 개의 <<를 이어 붙여 텍스트, 숫자, 변수를 자유롭게 섞을 수 있습니다.

<<는 개행을 삽입하기 전까지 같은 줄에 덧붙습니다. 방법은 두 가지입니다. '\n'은 개행 문자를 삽입하고, endl은 개행을 삽입하면서 버퍼를 화면으로 플러시까지 합니다. 플러시에는 실제 비용이 있으므로, 수천 줄을 출력하는 루프 안에서는 '\n'을 선호하세요. 스트림은 필요할 때 스스로 플러시합니다(그리고 프로그램 종료 시에는 항상).

// 한 번뿐인 메시지에는 괜찮습니다:
cout << "Done" << endl;

// 뜨거운 루프에서는 이것을 선호하세요 - 반복마다 강제 플러시 없음:
for (int i = 0; i < 1000000; ++i)
    cout << i << '\n';

cin과 >>로 읽기

cin >> variable은 공백으로 구분된 토큰 하나를 읽어 변수의 타입으로 변환합니다. 앞쪽의 공백이나 개행은 건너뛴 뒤, 다음 공백에서 멈춥니다.

>>는 공백에서 멈추기 때문에 개별 숫자나 단어 하나에는 훌륭하지만, 문장 전체에는 쓸모가 없습니다. 입력 hello world에 대한 cin >> wordhello만 읽고 world는 다음 읽기를 위해 버퍼에 남겨 둡니다.

getline으로 한 줄 전체 읽기

한 줄 전체를 — 공백까지 모두 — 잡아내려면 getline(cin, line)을 쓰세요. 이는 Enter 키까지의 모든 것을 std::string으로 읽어 들입니다.

입력에 공백이 들어갈 수 있을 때 — 이름, 주소, 문장 — 에는 이것이 올바른 도구입니다. 다만 getline>>와 섞는 순간 여러분을 기다리는 함정이 딱 하나 있습니다.

cin + getline 개행 함정

이것은 C++에서 단연 가장 흔한 I/O 버그입니다. cin >> n을 하면 추출은 숫자를 읽지만, 개행(여러분이 누른 Enter 키)을 입력 버퍼에 남겨 둡니다. 다음 getline은 그 남은 개행을 즉시 보고 줄이 이미 끝난 것으로 취급하여, 입력을 기다리며 멈추는 일 없이 빈 문자열을 건넵니다.

int age;
string city;

cin >> age;            // 30을 입력하고 Enter를 누르면 '\n'이 버퍼에 남음
getline(cin, city);    // 남은 '\n'을 읽음 -> city는 "" 가 됨(비어 있음!)

해결책은 그 남은 개행을 >> 다음, getline 앞에서 cin.ignore로 버리는 것입니다.

cin.ignore(numeric_limits<streamsize>::max(), '\n')는 개행 하나를 삼킬 때까지(또는 입력의 끝에 닿을 때까지) 문자를 건너뜁니다. 이것이 견고한 버전입니다. 더 짧은 cin.ignore()는 문자 하나만 버리는데, 사용자가 숫자 뒤에 공백을 더 입력하면 깨집니다. 습관적으로 완전한 형태를 쓰세요.

입력이 실패할 때

숫자를 기대한 자리에 사용자가 글자를 입력하면 추출이 실패합니다. cin은 오류 상태에 들어가고 대상 변수는 변경되지 않은 채 남습니다(C++11부터는 0으로 설정됩니다). 더 나쁜 것은, cin이 일단 실패 상태가 되면 이후의 모든 읽기도 조용히 건너뛰어진다는 점입니다. 그래서 무한 루프에 빠질 수 있습니다.

회복은 언제나 두 단계입니다. cin.clear()는 오류 플래그를 초기화하여 스트림을 다시 쓸 수 있게 하고, cin.ignore(...)는 버퍼에 여전히 걸려 있는 문제의 문자들을 버립니다. ignore를 건너뛰면 잘못된 입력이 그대로 남아 다음 >>가 또 실패합니다 — 고전적인 무한 루프죠. cin >> n을 조건에서 직접 검사할 수 있는 것은, 스트림이 실패 상태일 때 false로 변환되기 때문입니다.

피해야 할 흔한 실수

  • 문장에 cin >> s를 쓰기. 첫 공백에서 멈춥니다. 공백이 있는 것에는 getline을 쓰세요.
  • >>getline 사이에 cin.ignore를 잊기. 남은 개행이 빈 줄을 줍니다. 먼저 버퍼를 비우세요.
  • 어디서나 endl에 손대기. 하나하나가 플러시를 강제합니다. 기본은 '\n'으로 하고, endl은 출력이 정말 지금 나와야 할 때를 위해 아껴 두세요.
  • 실패한 cin을 무시하기. 숫자 읽기에서의 글자는 cin을 망가뜨립니다. 다시 읽기 전에 항상 clear()ignore()를 하세요.

다음: 문자열 스트림

콘솔 I/O와 문자열 처리는 문자열 스트림에서 하나로 만납니다. stringstream은 같은 <<>> 연산자를 주지만, 콘솔이 아니라 메모리 속 문자열을 향합니다 — 한 줄을 숫자로 파싱하고, 형식화된 텍스트를 쌓고, 키보드를 전혀 건드리지 않고 문자열과 다른 타입 사이를 변환하기에 안성맞춤입니다.

자주 묻는 질문

C++에서 cin을 쓴 직후에 getline이 입력을 건너뛰는 이유는 무엇인가요?

cin >> x는 숫자를 읽지만, 여러분이 누른 개행을 버퍼에 남겨 둡니다. 다음 getline은 그 남은 개행까지 읽고 곧바로 빈 문자열을 반환합니다. >> 다음, getline 앞에서 cin.ignore(numeric_limits<streamsize>::max(), '\n');로 먼저 지워 주세요.

C++에서 endl과 \n의 차이는 무엇인가요?

둘 다 줄을 끝내지만, endl은 출력 버퍼를 화면으로 플러시(flush) 까지 합니다. 반면 '\n'은 개행만 삽입합니다. 플러시에는 비용이 있으므로, 빡빡한 루프 안에서는 '\n'을 선호하고 스트림이 스스로 플러시하도록 두세요. endl은 출력이 정말 지금 당장 나타나야 할 때만 쓰세요.

C++에서 공백을 포함한 텍스트 한 줄 전체를 읽으려면 어떻게 하나요?

cin >> line이 아니라 getline(cin, line)을 쓰세요. >> 연산자는 첫 공백에서 멈추므로 단어 하나만 가져옵니다. getline은 Enter 키까지의 모든 것을 공백을 포함해 std::string으로 읽어 들입니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기