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.
- Por que
std::cin >> nomelê só uma palavra. - Como ler uma frase inteira com
std::getline(std::cin, linha). - Por que
cin >> idadeseguido degetlinelê uma linha vazia, e como consertar comstd::cin.ignore()oustd::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:
| Leitura | Regra | Pega da fila acima | Deixa na fila |
|---|---|---|---|
std::cin >> nome | Pula espaços do começo, pega até o próximo espaço ou Enter | Ana | ␣Maria\n |
std::getline(std::cin, nome) | Pega tudo até o Enter; tira o Enter da fila e joga fora | Ana Maria | nada |
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
getlinelê de arquivos, trocando ostd::cinpor 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
getlinelê até o'\n', tira o'\n'da fila e não o guarda. Por issoAna Maria Souzatem 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:
- A Ana digita
30e Enter. A fila fica:3 0 \n. std::cin >> idadepega o30e para antes do\n, que fica na fila:\n.std::getlinelê "tudo até o Enter". O primeiro caractere da fila já é um Enter! Ele lê zero caracteres, tira o\ne 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 oignore, 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
cintodos os espaços em branco e jogue fora". O resultado continua sendo o própriostd::cin, que é entregue aogetline. - std::getline(…, nome)
- Agora a fila começa no
AdeAna, e ogetlinelê a linha certa.
Qual usar?
| Forma | Vantagem | Cuidado |
|---|---|---|
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. |
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 owhiletermina. - 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
\nno ú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 (
ide input) cujo conteúdo é alinha. Para ogetline, ela se comporta igual aostd::cin. - std::getline(partes, nome, ',')
- Lê de
partesaté a vírgula:Ana. O próximogetlinecontinua 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
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');.
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).
[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
| Escrita | O 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:
2. Pular a sobra
Complete para o getline pular os espaços e Enters que o >> deixou:
3. Separador
Complete para ler um item até o próximo ponto e vírgula:
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 >> slê 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 ogetlineseguinte lê uma linha vazia. Conserte comstd::getline(std::cin >> std::ws, s)oustd::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 comstd::istringstream.getlinesó lê parastd::string.