Menu

C++ getline: 한 줄 전체 읽기, 입력이 건너뛰어지는 버그 해결, 파일 읽기

std::getline이 한 줄 전체를 std::string으로 읽는 방법: cin과 함께 쓰기, cin >> 다음에 빈 문자열이 반환되는 이유, cin.ignore와 std::ws로 고치는 법, 사용자 지정 구분자, 파일 끝까지 한 줄씩 읽기.

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

한 줄 전체 읽기

cin >> text는 단어 하나만 읽습니다. 첫 공백에서 멈추기 때문입니다. 사용자가 Enter 키까지 입력한 모든 것을 공백까지 포함해 읽으려면 <string> 헤더의 std::getline을 쓰세요:

#include <iostream>
#include <string>

int main() {
    std::string name;
    std::cout << "Full name: ";
    std::getline(std::cin, name);
    std::cout << "Hello, " << name << "!\n";
}

Ada Lovelace를 입력하면 Hello, Ada Lovelace!가 출력됩니다. std::cin >> name이었다면 Hello, Ada!를 출력하고 Lovelace는 입력에 남아 기다리고 있을 것입니다.

시그니처는 다음과 같습니다:

std::istream& std::getline(std::istream& input, std::string& text);
std::istream& std::getline(std::istream& input, std::string& text, char delim);
  • input은 어떤 입력 스트림이든 됩니다: std::cin, std::ifstream으로 연 파일, 문자열로 만든 std::istringstream.
  • text는 읽은 문자들로 교체됩니다(뒤에 덧붙지 않습니다).
  • delim은 읽기를 끝내는 문자입니다. 기본값은 '\n'입니다. 구분자는 스트림에서 제거되고 text에는 저장되지 않습니다.
  • 반환값은 스트림 자체이고, 그래서 조건문에서 검사할 수 있습니다.

getline은 모든 입력 스트림에서 똑같이 동작하므로, 이 페이지의 실행 가능한 예제들은 고정된 텍스트를 담은 std::istringstream에서 읽습니다. 그러면 아무것도 입력하지 않아도 출력을 예측할 수 있습니다. 키보드에서 읽으려면 스트림 변수를 std::cin으로 바꾸세요.

출력:

[Ada Lovelace]
[Alan Turing]

대괄호를 보면 개행이 두 문자열 어디에도 포함되지 않았다는 것을 알 수 있습니다.

cin >> 다음의 getline이 빈 문자열을 반환하는 문제

가장 흔한 getline 문제입니다. >>로 숫자를 읽고 getline으로 한 줄을 읽으면, getline이 건너뛰어진 것처럼 보입니다:

출력:

age: 30
city: []

input >> age는 문자 30을 읽고, 숫자의 일부가 될 수 없는 첫 문자인 개행에서 멈춥니다. 그 개행은 스트림에 남습니다. 그러면 getline은 다음 개행까지 읽는데, 곧바로 개행을 찾으니 빈 문자열을 반환합니다. std::cin에서는 프로그램이 두 번째 답을 기다리며 멈추지 않는 것으로 나타납니다.

표준적인 해결책은 두 가지입니다. 둘 다 >>와 getline 사이에 넣습니다.

해결책 1: ignore로 줄의 나머지를 버리기.

출력:

age: 30
city: [Paris]

ignore(n, '\n')는 개행 하나를 제거하거나 n개의 문자를 제거할 때까지, 둘 중 먼저 오는 쪽까지 문자를 버립니다. 가장 큰 std::streamsize 값을 넘기면 "개행이 얼마나 멀리 있든 거기까지"라는 뜻이 됩니다. 사용자가 같은 줄에서 숫자 뒤에 입력한 것, 예를 들어 뒤쪽 공백도 함께 제거합니다. 키보드에서는 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');이라고 쓰고, #include <limits>가 필요합니다.

인자 없는 std::cin.ignore()는 정확히 한 문자만 버립니다. 숫자 바로 뒤에 개행이 오면 동작하지만, 그 앞에 공백이 하나라도 있으면 실패합니다.

해결책 2: std::ws로 앞쪽 공백 건너뛰기.

출력:

age: 30
city: [Paris]

std::ws는 현재 위치의 모든 공백 문자(스페이스, 탭, 개행)를 소비하는 조작자입니다. input >> std::ws는 스트림을 반환하므로 바로 getline에 넣을 수 있습니다. 빈 줄과 앞쪽 공백도 건너뛰는데, 이름이나 도시에는 편리하지만 앞쪽 공백이나 빈 줄이 의미 있는 입력일 때는 잘못된 선택입니다. 그런 경우에는 ignore를 쓰세요.

입력 끝까지 읽기

getline은 스트림을 반환하고, 스트림은 읽기가 실패하면 false로 변환됩니다. 더 읽을 줄이 없으면 getline이 실패하므로, getline을 감싼 while 루프는 모든 줄을 읽고 끝에서 멈춥니다:

출력:

1: [apples 3]
2: [bread 1]
3: []
4: [milk 2]
read 4 lines

이 출력에서 볼 점 두 가지:

  • 빈 줄도 성공한 읽기입니다. 빈 문자열을 주고 루프는 계속됩니다. 루프를 멈추는 것은 입력의 끝에 도달하는 것뿐입니다.
  • 마지막 줄에는 끝에 개행이 없어도 됩니다. 텍스트가 \n 없이 milk 2로 끝났더라도 getline은 그 줄을 반환하고, 그다음 호출이 실패합니다.

std::cin에서는 같은 루프가 입력이 끝날 때까지 읽습니다. 입력이 끝나는 것은 파일에서 파이프로 입력을 받을 때(./program < data.txt), 또는 사용자가 macOS와 Linux에서 Ctrl+D를, Windows에서 Ctrl+Z 다음 Enter를 누를 때입니다.

while (!input.eof()) { std::getline(input, line); ... } 패턴은 피하세요. eof()는 읽기가 이미 끝에 닿은 뒤에야 참이 되기 때문에, 본문이 실패한 읽기와 이전 값이 남은 line으로 한 번 더 실행됩니다. 대신 getline 호출을 조건에 넣으세요.

파일을 한 줄씩 읽기

파일에도 같은 루프를 씁니다. <fstream>의 std::ifstream으로 파일을 열고, 열렸는지 확인한 뒤 읽으세요:

출력:

1. Ada 92
2. Grace 88
3. Alan 95

if (!file)은 루프 전에 파일이 없거나 경로가 잘못된 경우를 잡아냅니다. 이 검사가 없으면 루프는 그냥 0줄을 읽고, 오류가 아니라 빈 파일처럼 보입니다. 파일은 main이 끝나 file이 스코프를 벗어날 때 자동으로 닫힙니다.

Windows에서 저장한 파일은 각 줄이 \r\n으로 끝납니다. 그런 파일을 Linux나 macOS에서 읽으면 getline이 \n은 제거하지만 \r은 남기기 때문에, 모든 줄 끝에 보이지 않는 캐리지 리턴이 붙어 line == "done" 같은 비교가 깨집니다. 읽은 다음에 제거하세요:

if (!line.empty() && line.back() == '\r') {
    line.pop_back();
}

사용자 지정 구분자 쓰기

세 번째 인자는 각 읽기를 끝내는 문자를 바꿉니다. 흔한 용도는 한 줄을 쉼표로 나누는 것입니다. 먼저 줄을 읽고, 그다음 std::istringstream을 통해 나누세요:

출력:

fields: 3
[Ada Lovelace]
[36]
[London]

기본 형태에서 개행이 그렇듯, 쉼표는 스트림에서 제거되고 저장되지 않습니다. 사용자 지정 구분자를 쓰면 개행은 평범한 문자가 된다는 점에 주의하세요. 여러 줄짜리 파일에 std::getline(file, field, ',')를 직접 호출하면, 한 줄의 마지막 필드와 다음 줄의 첫 필드가 "London\nGrace Hopper"처럼 붙어서 돌아옵니다. 한 번에 한 줄씩 읽고 각 줄을 따로 나누면 이 문제를 피할 수 있습니다. std::istringstream은 "36" 같은 필드를 숫자로 변환하는 방법까지 포함해 문자열 스트림 페이지에서 더 자세히 다룹니다.

std::getline vs cin.getline

이름이 비슷한 멤버 함수도 있습니다:

char buffer[20];
std::cin.getline(buffer, 20);        // 멤버 함수, 고정 크기 char 배열

std::string text;
std::getline(std::cin, text);        // 자유 함수, std::string

cin.getline은 char 배열에 쓰고, 최대 size - 1개의 문자와 끝을 나타내는 '\0'을 저장합니다. 줄이 더 길면 스트림이 실패 상태가 되고, std::cin.clear()를 호출할 때까지 이후의 모든 읽기가 건너뛰어집니다. std::string과 함께 쓰는 std::getline에는 신경 쓸 길이 제한이 없습니다. char 배열을 요구하는 API를 다루는 경우가 아니라면 이쪽을 쓰세요.

흔한 실수

  • 개행을 지우지 않고 >>와 getline을 섞어 쓰기. 둘 사이에 ignore(...)를 넣거나 std::ws를 쓰세요. std::cin에서 생기는 같은 문제와 숫자 읽기가 실패했을 때 회복하는 방법은 입력과 출력 페이지에서 보여 줍니다.
  • int로 getline(std::cin, number) 호출하기. getline은 std::string으로만 읽습니다. 줄을 읽은 다음 std::stoi나 std::istringstream으로 변환하세요.
  • eof()로 루프 돌기. getline 호출 자체를 검사하세요: while (std::getline(input, line)).
  • #include <string> 빠뜨리기. 문자열용 std::getline은 <string>에 선언되어 있습니다. 어떤 컴파일러는 <iostream>을 통해 그냥 넘어가게 해 주는데, 그러면 다른 컴파일러에서 코드가 실패합니다.
  • 파일이 열렸는지 확인하지 않기. 경로가 틀리면 std::ifstream은 조용히 실패하고 읽기 루프는 0번 실행됩니다.

자주 묻는 질문

C++에서 getline은 무엇을 하나요?

std::getline(stream, text)는 개행을 만날 때까지 stream에서 문자를 읽어 std::string인 text에 담고, 그 개행은 저장하지 않고 스트림에서 제거합니다. cin >> text와 달리 공백을 유지하므로, 전체 이름이나 문장 같은 한 줄 전체를 읽습니다.

C++에서 cin >> 다음의 getline이 건너뛰어지는 이유는 무엇인가요?

cin >> number는 숫자 뒤에 입력한 개행에서 읽기를 멈추고, 그 개행을 입력 버퍼에 남겨 둡니다. 다음 getline은 곧바로 그 개행을 찾아 빈 문자열을 반환합니다. 먼저 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');로 버리거나, std::getline(std::cin >> std::ws, text)를 호출해 앞쪽 공백을 건너뛰세요.

개행이 아닌 다른 구분자로 getline을 쓰려면 어떻게 하나요?

구분자를 세 번째 인자로 넘기세요: std::getline(stream, field, ','). 다음 쉼표까지 읽어서 그 앞의 텍스트를 저장하고 쉼표는 버립니다. 이때 개행은 평범한 문자로 취급되므로, 보통 한 줄로 만든 std::stringstream과 함께 씁니다.

C++에서 파일을 한 줄씩 읽으려면 어떻게 하나요?

std::ifstream file("data.txt");로 열고 while (std::getline(file, line)) { ... }로 반복하세요. getline은 스트림을 반환하고, 더 읽을 줄이 없으면 그 스트림이 false로 변환되므로 루프는 파일 끝에서 멈춥니다.

cin.getline과 getline의 차이는 무엇인가요?

std::getline(std::cin, text)는 std::string으로 읽는 자유 함수이고, 문자열은 어떤 길이의 줄이든 담을 수 있게 늘어납니다. std::cin.getline(buffer, size)는 고정 크기 char 배열로 읽는 멤버 함수이고, 줄이 size - 1자보다 길면 실패합니다. 원시 배열을 써야 할 이유가 없다면 std::string과 함께 std::getline을 쓰세요.

Coddy 프로그래밍 언어 일러스트

Coddy로 코딩 배우기

시작하기