C++ Ler uma Linha (getline)

Na aula Entrada com cin você leu números e palavras do teclado com std::cin >>. Mas tente pedir o nome completo da Ana: o programa só fica com Ana. O >> para no primeiro espaço. Para ler uma linha inteira, com espaços, existe o std::getline. Nesta aula você vai usá-lo, vai conhecer o bug mais famoso de quem mistura >> com getline (e as três formas de consertar), e vai ler várias linhas, até o fim da entrada.

Você vai aprender
  • Por que std::cin >> nome lê só uma palavra.
  • Como ler uma frase inteira com std::getline(std::cin, linha).
  • Por que cin >> idade seguido de getline lê uma linha vazia, e como consertar com std::cin.ignore() ou std::ws.
  • Como ler todas as linhas até o fim da entrada com while (std::getline(…)), e o que é o Ctrl+D.
  • Como usar outro separador no lugar do Enter: getline(…, ',').

Todos os programas desta aula foram compilados com o g++ 15.2 na máquina fictícia da Ana, na pasta ~/cpp, com o comando padrão g++ -std=c++23 -Wall -Wextra arquivo.cpp -o programa. Nas sessões de terminal, o que vem depois de Nome completo: ou Idade: foi digitado pela Ana, seguido de Enter. Nos editores do guia, a entrada já vem preenchida (ela aparece embaixo do código).


A ideia: o carteiro e a fila de letras

Quando você digita no terminal, as letras não vão direto para o programa. Elas entram numa fila (o buffer de entrada) e ficam esperando. O Enter também entra na fila, como um caractere invisível: o '\n'. Se a Ana digita Ana Maria e aperta Enter, a fila fica assim:

A n a ␣ M a r i a \n

Cada forma de leitura é um "carteiro" com uma regra diferente para pegar letras dessa fila:

LeituraRegraPega da fila acimaDeixa na fila
std::cin >> nomePula espaços do começo, pega até o próximo espaço ou EnterAna␣Maria\n
std::getline(std::cin, nome)Pega tudo até o Enter; tira o Enter da fila e joga foraAna Marianada

Guarde a última coluna: é ela que explica o bug da seção 3.


1. O problema: >> lê uma palavra

#include <iostream>
#include <string>

int main() {
    std::string nome;
    std::cout << "Nome completo: ";
    std::cin >> nome;
    std::cout << "Olá, " << nome << "!\n";
}
$ g++ -std=c++23 -Wall -Wextra cinpalavra.cpp -o cinpalavra
$ ./cinpalavra
Nome completo: Ana Maria Souza
Olá, Ana!          « o resto se perdeu

O >> foi feito para ler valores separados por espaço: std::cin >> a >> b lê dois números digitados como 3 4. Para ele, o espaço é um separador, e Maria Souza ficou esperando na fila para uma próxima leitura que nunca veio.


2. std::getline: a linha inteira

#include <iostream>
#include <string>

int main() {
    std::string nome;
    std::cout << "Nome completo: ";
    std::getline(std::cin, nome);
    std::cout << "Olá, " << nome << "!\n";
    std::cout << "(" << nome.size() << " caracteres)\n";
}
$ g++ -std=c++23 -Wall -Wextra linha.cpp -o linha
$ ./linha
Nome completo: Ana Maria Souza
Olá, Ana Maria Souza!
(15 caracteres)    « o Enter não entra na string
std::getline
"Pegar linha" (get line). Vem do cabeçalho <string>.
std::cin
De onde ler: a entrada padrão, o teclado. (O mesmo getline lê de arquivos, trocando o std::cin por um arquivo aberto; veja Ler e Escrever Arquivos.)
nome
Onde guardar: sempre um std::string. O texto antigo da variável é trocado pela linha lida.
O Enter
O getline lê até o '\n', tira o '\n' da fila e não o guarda. Por isso Ana Maria Souza tem 15 caracteres, sem nenhum Enter no fim.

O getline guarda a linha exatamente como foi digitada: espaços no começo, no meio e no fim, tudo. Se a pessoa só apertar Enter, a string fica vazia, e você pode conferir com .empty() (aula Strings).

Missão: este programa só guarda a primeira palavra do nome. Troque a leitura por std::getline para ele mostrar Olá, Ana Maria Souza!. A entrada (o que a "Ana digitou") já está preenchida.

#include <iostream>
#include <string>

int main() {
    std::string nome;
    std::cout << "Nome completo: ";
    std::cin >> nome;
    std::cout << "Olá, " << nome << "!\n";
}
#include <iostream>
#include <string>

int main() {
    std::string nome;
    std::cout << "Nome completo: ";
    std::getline(std::cin, nome);
    std::cout << "Olá, " << nome << "!\n";
}

3. O bug clássico: cin >> idade e depois getline

Agora o programa pede a idade (um número, com >>) e depois o nome completo (com getline). Parece perfeito:

#include <iostream>
#include <string>

int main() {
    int idade = 0;
    std::string nome;
    std::cout << "Idade: ";
    std::cin >> idade;
    std::cout << "Nome completo: ";
    std::getline(std::cin, nome);
    std::cout << "[" << nome << "] tem " << idade << " anos\n";
}
$ g++ -std=c++23 -Wall -Wextra bug.cpp -o bug
$ ./bug
Idade: 30
Nome completo: [] tem 30 anos     « nem deu tempo de digitar o nome!

A Ana digitou 30 e Enter. O programa mostrou Nome completo: e, sem esperar, terminou com o nome vazio. Nenhum erro, nenhum aviso. O que aconteceu? Siga a fila:

  1. A Ana digita 30 e Enter. A fila fica: 3 0 \n.
  2. std::cin >> idade pega o 30 e para antes do \n, que fica na fila: \n.
  3. std::getline lê "tudo até o Enter". O primeiro caractere da fila já é um Enter! Ele lê zero caracteres, tira o \n e devolve uma linha vazia, na hora.

O getline fez exatamente o que promete: leu o resto da linha do 30, que estava vazio. O problema é a sobra que o >> deixou na fila. A solução, portanto, é jogar a sobra fora antes do getline.


4. As correções

Forma 1: std::cin.ignore()

ignore ("ignorar") tira um caractere da fila e joga fora. Coloque-o logo depois do >>:

#include <iostream>
#include <string>

int main() {
    int idade = 0;
    std::string nome;
    std::cout << "Idade: ";
    std::cin >> idade;
    std::cin.ignore();
    std::cout << "Nome completo: ";
    std::getline(std::cin, nome);
    std::cout << "[" << nome << "] tem " << idade << " anos\n";
}
$ g++ -std=c++23 -Wall -Wextra ignore.cpp -o ignore
$ ./ignore
Idade: 30
Nome completo: Ana Maria
[Ana Maria] tem 30 anos

Funciona, mas tem um ponto fraco: ele joga fora um caractere só. Se a Ana digitar 30, uns espaços e só depois o Enter, a sobra é maior que um caractere:

$ ./ignore
Idade: 30
Nome completo: [  ] tem 30 anos   « sobraram 2 espaços e o Enter

Forma 2: std::cin.ignore(…, '\n'), jogar fora até o Enter

Dá para pedir: "ignore quantos caracteres for preciso, até passar por um '\n'". O "quantos for preciso" se escreve com o maior número possível, que vem de <limits>:

#include <iostream>
#include <limits>
#include <string>

int main() {
    int idade = 0;
    std::string nome;
    std::cout << "Idade: ";
    std::cin >> idade;
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cout << "Nome completo: ";
    std::getline(std::cin, nome);
    std::cout << "[" << nome << "] tem " << idade << " anos\n";
}
$ g++ -std=c++23 -Wall -Wextra ignoremax.cpp -o ignoremax
$ ./ignoremax
Idade: 30
Nome completo: Ana Maria
[Ana Maria] tem 30 anos     « os espaços depois do 30 também foram descartados
std::numeric_limits<std::streamsize>::max()
"O maior valor possível do tipo std::streamsize" (o tipo que a entrada usa para contar caracteres). Para o ignore, esse valor quer dizer "sem limite". É comprido, mas é sempre igual: copie.
'\n'
O delimitador: "pare depois de jogar fora um Enter".

Forma 3: std::ws, pular os espaços em branco

A forma mais curta: std::ws (whitespace, "espaço em branco") come todos os espaços, tabs e Enters do começo da fila. Ele vai dentro do getline:

#include <iostream>
#include <string>

int main() {
    int idade = 0;
    std::string nome;
    std::cout << "Idade: ";
    std::cin >> idade;
    std::cout << "Nome completo: ";
    std::getline(std::cin >> std::ws, nome);
    std::cout << "[" << nome << "] tem " << idade << " anos\n";
}
$ g++ -std=c++23 -Wall -Wextra ws.cpp -o ws
$ ./ws
Idade: 30
Nome completo: Ana Maria
[Ana Maria] tem 30 anos
std::cin >> std::ws
"Leia do cin todos os espaços em branco e jogue fora". O resultado continua sendo o próprio std::cin, que é entregue ao getline.
std::getline(…, nome)
Agora a fila começa no A de Ana, e o getline lê a linha certa.

Qual usar?

FormaVantagemCuidado
std::getline(std::cin >> std::ws, s)Curta e resistente a espaços e linhas em branco. A recomendada para quem começa.Também apaga espaços do começo da linha e pula linhas vazias. Não serve se uma linha vazia é uma resposta válida.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');Descarta exatamente o resto da linha do >>, e nada mais.Comprida; precisa de <limits>.
std::cin.ignore();A mais curta.Só descarta 1 caractere: falha com espaços depois do número.
Uma regra que evita o problema

Muitos programadores preferem não misturar: leem tudo com getline (inclusive a idade, como texto) e convertem para número depois, com std::stoi. Assim nunca sobra nada na fila. A conversão está em Texto ↔ Número, e a validação completa da entrada em Validar a Entrada.


5. Ler até o fim da entrada

E quando você não sabe quantas linhas virão? Uma lista de compras, um arquivo inteiro? O getline responde "deu certo" ou "acabou", e por isso pode ir direto num while ("enquanto", aula while): "enquanto conseguir ler uma linha, faça isto".

#include <iostream>
#include <string>

int main() {
    std::string linha;
    int numero = 0;
    while (std::getline(std::cin, linha)) {
        numero++;
        std::cout << numero << ": " << linha << '\n';
    }
    std::cout << "Total: " << numero << " linhas\n";
}
$ g++ -std=c++23 -Wall -Wextra linhas.cpp -o linhas
$ ./linhas
arroz              « digitado
1: arroz
feijão             « digitado
2: feijão
                   « aqui a Ana apertou Ctrl+D
Total: 2 linhas
while (std::getline(std::cin, linha))
Tenta ler uma linha. Se conseguiu, roda o bloco e tenta de novo. Se a entrada acabou, o getline "falha", e o while termina.
Ctrl+D
No teclado, a entrada nunca "acaba" sozinha: a pessoa avisa que terminou apertando Ctrl+D no começo de uma linha (no Linux e no macOS; no Windows, Ctrl+Z e Enter). Esse aviso se chama fim de arquivo (EOF, end of file). Ele não aparece na tela.

O mesmo programa lê um arquivo inteiro, sem mudar nada, se você redirecionar a entrada no terminal com < ou mandar o texto por um | (o "cano" que liga a saída de um comando à entrada de outro; o Guia Bash explica os dois na aula de redirecionamento). Aí o fim de arquivo chega sozinho, quando o arquivo termina:

$ cat compras.txt
arroz
feijão
café com leite
$ ./linhas < compras.txt
1: arroz
2: feijão
3: café com leite
Total: 3 linhas
$ cat compras.txt | ./linhas
1: arroz
2: feijão
3: café com leite
Total: 3 linhas

Experimente: a entrada tem quatro linhas, uma delas vazia. Rode e veja como a linha vazia aparece. Depois, mude o programa para não contar as linhas vazias: dentro do while, só faça algo if (!linha.empty()).

#include <iostream>
#include <string>

int main() {
    std::string linha;
    int numero = 0;
    while (std::getline(std::cin, linha)) {
        numero++;
        std::cout << numero << ": [" << linha << "]\n";
    }
    std::cout << "Total: " << numero << " linhas\n";
}

6. Outro separador: getline(…, ',')

O getline aceita um terceiro argumento: o caractere que marca o fim de cada pedaço, no lugar do Enter. Com ',', ele lê "até a próxima vírgula":

#include <iostream>
#include <string>

int main() {
    std::string item;
    while (std::getline(std::cin, item, ',')) {
        std::cout << "[" << item << "]\n";
    }
}
$ g++ -std=c++23 -Wall -Wextra virgula.cpp -o virgula
$ printf 'arroz,feijão,café' | ./virgula
[arroz]
[feijão]
[café]
$ printf 'arroz,feijão,café\n' | ./virgula
[arroz]
[feijão]
[café              « o Enter do fim entrou no último item!
]
std::getline(std::cin, item, ',')
Lê até a vírgula, joga a vírgula fora e guarda o resto em item. O último pedaço vai até o fim da entrada.
O \n no último item
Com o separador trocado, o Enter passa a ser um caractere comum, e vai parar dentro do último pedaço. É uma armadilha clássica: "café\n" == "café" dá false.

A forma segura de ler uma linha com campos separados por vírgula (um arquivo CSV, como os de planilha) é em duas etapas: primeiro a linha inteira, com o getline normal (que já descarta o Enter); depois, separar a linha nas vírgulas. Para isso, você transforma a linha numa "entrada de mentira" com std::istringstream, e o getline lê dela como leria do teclado:

#include <iostream>
#include <sstream>
#include <string>

int main() {
    std::string linha;
    while (std::getline(std::cin, linha)) {
        std::istringstream partes(linha);
        std::string nome, nota;
        std::getline(partes, nome, ',');
        std::getline(partes, nota, ',');
        std::cout << nome << " tirou " << nota << '\n';
    }
}
$ g++ -std=c++23 -Wall -Wextra csv.cpp -o csv
$ cat notas.csv
Ana,9.5
Bruno,7.0
Carla,8.25
$ ./csv < notas.csv
Ana tirou 9.5
Bruno tirou 7.0
Carla tirou 8.25
#include <sstream>
Traz os string streams: "fluxos" que leem de (ou escrevem numa) string, em vez do teclado ou da tela.
std::istringstream partes(linha);
Cria uma entrada (i de input) cujo conteúdo é a linha. Para o getline, ela se comporta igual ao std::cin.
std::getline(partes, nome, ',')
Lê de partes até a vírgula: Ana. O próximo getline continua dali: 9.5.

As notas chegaram como texto ("9.5"). Para fazer contas com elas, converta com std::stod, em Texto ↔ Número, que também fala mais do std::stringstream.


Quando dá errado

O getline não espera e a string fica vazia

O que significa: antes dele houve um std::cin >> …, que deixou o Enter na fila. O getline leu esse Enter como uma linha vazia.

Como consertar: std::getline(std::cin >> std::ws, s); ou, logo depois do >>, std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');.

Só a primeira palavra foi guardada

O que significa: a leitura foi feita com std::cin >> s, que para no primeiro espaço. O resto ficou na fila e pode "atropelar" a próxima leitura.

Como consertar: use std::getline(std::cin, s).

O último item tem um Enter grudado ([café e ] em linhas separadas)

O que significa: você usou getline(…, ',') direto na entrada. Com outro separador, o '\n' vira um caractere comum.

Como consertar: leia a linha com o getline normal e separe as vírgulas com std::istringstream (seção 6).

error: no matching function for call to ‘getline(std::istream&, int&)’
#include <iostream>
#include <string>

int main() {
    int idade = 0;
    std::getline(std::cin, idade);
    std::cout << idade << '\n';
}
$ g++ -std=c++23 -Wall -Wextra getint.cpp -o getint
getint.cpp: In function ‘int main()’:
getint.cpp:6:17: error: no matching function for call to ‘getline(std::istream&, int&)’
    6 |     std::getline(std::cin, idade);
      |     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
getint.cpp:6:17: note: there are 4 candidates
In file included from /usr/include/c++/15/string:57,
                 from /usr/include/c++/15/bits/locale_classes.h:42,
                 from /usr/include/c++/15/bits/ios_base.h:43,
                 from /usr/include/c++/15/ios:46,
                 from /usr/include/c++/15/bits/ostream.h:43,
                 from /usr/include/c++/15/ostream:42,
                 from /usr/include/c++/15/iostream:43,
                 from getint.cpp:1:
/usr/include/c++/15/bits/basic_string.tcc:938:5: note: candidate 1: ‘template<class _CharT, class _Traits, class _Alloc> std::basic_istream<_CharT, _Traits>& std::getline(basic_istream<_CharT, _Traits>&, __cxx11::basic_string<_CharT, _Traits, _Alloc>&, _CharT)’
  938 |     getline(basic_istream<_CharT, _Traits>& __in,
      |     ^~~~~~~
/usr/include/c++/15/bits/basic_string.tcc:938:5: note: candidate expects 3 arguments, 2 provided
In file included from /usr/include/c++/15/string:56:
/usr/include/c++/15/bits/basic_string.h:4405:5: note: candidate 2: ‘template<class _CharT, class _Traits, class _Alloc> std::basic_istream<_CharT, _Traits>& std::getline(basic_istream<_CharT, _Traits>&, __cxx11::basic_string<_CharT, _Traits, _Alloc>&)’
 4405 |     getline(basic_istream<_CharT, _Traits>& __is,
      |     ^~~~~~~
/usr/include/c++/15/bits/basic_string.h:4405:5: note: template argument deduction/substitution failed:
getint.cpp:6:17: note:   mismatched types ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>’ and ‘int’
    6 |     std::getline(std::cin, idade);
      |     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
/usr/include/c++/15/bits/basic_string.h:4413:5: note: candidate 3: ‘template<class _CharT, class _Traits, class _Alloc> std::basic_istream<_CharT, _Traits>& std::getline(basic_istream<_CharT, _Traits>&&, __cxx11::basic_string<_CharT, _Traits, _Alloc>&, _CharT)’
 4413 |     getline(basic_istream<_CharT, _Traits>&& __is,
      |     ^~~~~~~
/usr/include/c++/15/bits/basic_string.h:4413:5: note: candidate expects 3 arguments, 2 provided
/usr/include/c++/15/bits/basic_string.h:4420:5: note: candidate 4: ‘template<class _CharT, class _Traits, class _Alloc> std::basic_istream<_CharT, _Traits>& std::getline(basic_istream<_CharT, _Traits>&&, __cxx11::basic_string<_CharT, _Traits, _Alloc>&)’
 4420 |     getline(basic_istream<_CharT, _Traits>&& __is,
      |     ^~~~~~~
/usr/include/c++/15/bits/basic_string.h:4420:5: note: template argument deduction/substitution failed:
getint.cpp:6:17: note:   mismatched types ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>’ and ‘int’
    6 |     std::getline(std::cin, idade);
      |     ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~

O que significa: a primeira linha já diz tudo: "nenhuma função getline aceita (entrada, int)". O resto são as 4 versões que existem (os "candidatos"), e o motivo de cada uma ter sido recusada. A pista está em mismatched types ‘…basic_string…’ and ‘int’: o getline queria uma string e recebeu um int.

Como consertar: o getline só lê para std::string. Para um número, use std::cin >> idade, ou leia uma string e converta com std::stoi (Texto ↔ Número).


Referência rápida

EscritaO que faz
std::getline(std::cin, s);Lê uma linha inteira (com espaços) para s; descarta o Enter
std::getline(std::cin, s, ',');Lê até a vírgula (o Enter vira caractere comum)
std::getline(std::cin >> std::ws, s);Pula espaços e Enters que sobraram, depois lê a linha
std::cin.ignore();Descarta 1 caractere da entrada
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');Descarta até o fim da linha (<limits>)
while (std::getline(std::cin, s)) { … }Lê todas as linhas até o fim da entrada
Ctrl+D (Linux/macOS), Ctrl+Z Enter (Windows)Fim da entrada pelo teclado
std::istringstream p(linha);Uma entrada que lê de uma string (<sstream>)

Exercícios

1. Ler a frase

Complete para ler uma linha inteira do teclado para a string frase:

std::(std::cin, frase);

2. Pular a sobra

Complete para o getline pular os espaços e Enters que o >> deixou:

std::getline(std::cin >> std::, nome);

3. Separador

Complete para ler um item até o próximo ponto e vírgula:

std::getline(std::cin, item, );

4. A pessoa digita Bom dia (com dois espaços antes e dois depois) e Enter. Quanto vale s.size() depois de std::getline(std::cin, s)?

5. Num programa com std::cin >> n; seguido de std::getline(std::cin, s);, a pessoa digita 5 e Enter. O que acontece?

6. Como o while (std::getline(std::cin, linha)) termina quando a entrada vem do teclado?

Missão final: o cadastro que pulava o nome

Missão: o cadastro lê a idade e depois o nome completo, mas o nome sai vazio (é o bug da seção 3). Conserte com uma das formas da seção 4, para mostrar Ana Maria Souza tem 30 anos. A entrada é 30 numa linha e Ana Maria Souza na outra.

#include <iostream>
#include <string>

int main() {
    int idade = 0;
    std::string nome;
    std::cout << "Idade: ";
    std::cin >> idade;
    std::cout << "Nome completo: ";
    std::getline(std::cin, nome);
    std::cout << '\n' << nome << " tem " << idade << " anos\n";
}
#include <iostream>
#include <string>

int main() {
    int idade = 0;
    std::string nome;
    std::cout << "Idade: ";
    std::cin >> idade;
    std::cout << "Nome completo: ";
    std::getline(std::cin >> std::ws, nome);
    std::cout << '\n' << nome << " tem " << idade << " anos\n";
}

Resumo

  • std::cin >> s lê uma palavra; std::getline(std::cin, s) lê a linha inteira, com espaços, e descarta o Enter.
  • Depois de um >>, o Enter fica na fila, e o getline seguinte lê uma linha vazia. Conserte com std::getline(std::cin >> std::ws, s) ou std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n').
  • while (std::getline(std::cin, linha)) lê até o fim da entrada: Ctrl+D no teclado, ou o fim de um arquivo redirecionado com <.
  • getline(…, ',') usa outro separador, mas o Enter vira caractere comum. Para CSV, leia a linha e separe com std::istringstream.
  • getline só lê para std::string.