Por que isso acontece?
Isso tem pouco a ver com a entrada que você forneceu, mas com as std::getline()
exibições de comportamento padrão . Quando você forneceu sua entrada para o nome ( std::cin >> name
), não apenas enviou os seguintes caracteres, mas também uma nova linha implícita foi anexada ao fluxo:
"John\n"
Uma nova linha é sempre anexada à sua entrada quando você seleciona Enterou Returnao enviar de um terminal. Também é usado em arquivos para mover para a próxima linha. A nova linha é deixada no buffer após a extração name
até a próxima operação de E / S, onde é descartada ou consumida. Quando o fluxo de controle chegar std::getline()
, a nova linha será descartada, mas a entrada cessará imediatamente. A razão para isso acontecer é porque a funcionalidade padrão dessa função determina que ela deve (ela tenta ler uma linha e para quando encontra uma nova linha).
Como essa nova linha inibe a funcionalidade esperada de seu programa, ela deve ser ignorada ou ignorada de alguma forma. Uma opção é chamar std::cin.ignore()
após a primeira extração. Ele descartará o próximo caractere disponível para que a nova linha não atrapalhe.
std::getline(std::cin.ignore(), state)
Explicação detalhada:
Essa é a sobrecarga do std::getline()
que você chamou:
template<class charT>
std::basic_istream<charT>& getline( std::basic_istream<charT>& input,
std::basic_string<charT>& str )
Outra sobrecarga dessa função leva um delimitador de tipo charT
. Um caractere delimitador é um caractere que representa o limite entre as sequências de entrada. Essa sobrecarga específica define o delimitador para o caractere de nova linha input.widen('\n')
por padrão, pois um não foi fornecido.
Agora, essas são algumas das condições pelas quais std::getline()
termina a entrada:
- Se o stream extraiu a quantidade máxima de caracteres que um
std::basic_string<charT>
pode conter
- Se o caractere de fim de arquivo (EOF) foi encontrado
- Se o delimitador foi encontrado
A terceira condição é aquela com a qual estamos lidando. Sua entrada em state
é representada da seguinte forma:
"John\nNew Hampshire"
^
|
next_pointer
onde next_pointer
é o próximo caractere a ser analisado. Visto que o caractere armazenado na próxima posição na sequência de entrada é o delimitador, std::getline()
descartará silenciosamente esse caractere, aumentará next_pointer
para o próximo caractere disponível e interromperá a entrada. Isso significa que o restante dos caracteres fornecidos ainda permanecem no buffer para a próxima operação de E / S. Você notará que se realizar outra leitura da linha para dentro state
, sua extração produzirá o resultado correto como a última chamada parastd::getline()
descartar o delimitador.
Você deve ter notado que normalmente não encontra esse problema ao extrair com o operador de entrada formatado ( operator>>()
). Isso ocorre porque os fluxos de entrada usam espaços em branco como delimitadores de entrada e têm o std::skipws
1 manipulador ativado por padrão. Streams irá descartar o espaço em branco inicial do stream ao começar a realizar a entrada formatada. 2
Ao contrário dos operadores de entrada formatados, std::getline()
é uma função de entrada não formatada . E todas as funções de entrada não formatadas têm o seguinte código um tanto em comum:
typename std::basic_istream<charT>::sentry ok(istream_object, true);
O acima é um objeto sentinela que é instanciado em todas as funções de E / S formatadas / não formatadas em uma implementação C ++ padrão. Objetos de sentinela são usados para preparar o fluxo para E / S e determinar se ele está ou não em um estado de falha. Você descobrirá apenas que nas funções de entrada não formatadas , o segundo argumento para o construtor de sentinela é true
. Esse argumento significa que o espaço em branco inicial não será descartado do início da sequência de entrada. Aqui está a citação relevante do Padrão [§27.7.2.1.3 / 2]:
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
[...] Se noskipws
for zero e is.flags() & ios_base::skipws
for diferente de zero, a função extrai e descarta cada caractere, desde que o próximo caractere de entrada disponível c
seja um caractere de espaço em branco. [...]
Visto que a condição acima é falsa, o objeto sentinela não descartará o espaço em branco. O motivo noskipws
definido true
por essa função é porque o objetivo de std::getline()
é ler caracteres brutos e não formatados em um std::basic_string<charT>
objeto.
A solução:
Não há como impedir esse comportamento de std::getline()
. O que você terá que fazer é descartar a nova linha antes de std::getline()
executá-la (mas faça isso após a extração formatada). Isso pode ser feito usando ignore()
para descartar o resto da entrada até chegarmos a uma nova linha:
if (std::cin >> name &&
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n') &&
std::getline(std::cin, state))
{ ... }
Você precisará incluir <limits>
para usar std::numeric_limits
. std::basic_istream<...>::ignore()
é uma função que descarta uma quantidade especificada de caracteres até encontrar um delimitador ou chegar ao final do fluxo ( ignore()
também descarta o delimitador se o encontrar). A max()
função retorna a maior quantidade de caracteres que um fluxo pode aceitar.
Outra maneira de descartar o espaço em branco é usar a std::ws
função, que é um manipulador projetado para extrair e descartar o espaço em branco inicial do início de um fluxo de entrada:
if (std::cin >> name && std::getline(std::cin >> std::ws, state))
{ ... }
Qual é a diferença?
A diferença é que ignore(std::streamsize count = 1, int_type delim = Traits::eof())
3 descarta caracteres indiscriminadamente até que descarta os count
caracteres, encontre o delimitador (especificado pelo segundo argumento delim
) ou atinja o final do fluxo.std::ws
é usado apenas para descartar caracteres de espaço em branco do início do fluxo.
Se você estiver misturando entrada formatada com entrada não formatada e precisar descartar os espaços em branco residuais, use std::ws
. Caso contrário, se você precisar limpar a entrada inválida independentemente do que seja, use ignore()
. Em nosso exemplo, só precisamos limpar os espaços em branco, pois o fluxo consumiu sua entrada "John"
para a name
variável. Tudo o que restou foi o caractere de nova linha.
1: std::skipws
é um manipulador que diz ao fluxo de entrada para descartar os espaços em branco iniciais ao realizar a entrada formatada. Isso pode ser desligado com o std::noskipws
manipulador.
2: Os fluxos de entrada consideram certos caracteres como espaços em branco por padrão, como o caractere de espaço, caractere de nova linha, avanço de formulário, retorno de carro, etc.
3: Esta é a assinatura de std::basic_istream<...>::ignore()
. Você pode chamá-lo com zero argumentos para descartar um único caractere do fluxo, um argumento para descartar uma certa quantidade de caracteres ou dois argumentos para descartar count
caracteres ou até atingir delim
, o que vier primeiro. Você normalmente usa std::numeric_limits<std::streamsize>::max()
como o valor de count
se não souber quantos caracteres existem antes do delimitador, mas deseja descartá-los de qualquer maneira.
std::cin >> name && std::cin >> std::skipws && std::getline(std::cin, state)
que também deve funcionar conforme o esperado. (Além das respostas abaixo).