Subsecções

29 Entrada e Saída Padrão

A forma com que um programa em C++ se comunica com o mundo externo é através de entrada e saída de dados: o usuário fornece dados via teclado e o programa imprime mensagens na tela. Todos os programas vistos até agora lêem suas entradas do teclado e produzem suas saídas na tela.

Em C++ toda entrada e saída é feita com fluxos (streams) de caracteres organizados em linhas. Cada linha consiste de zero ou mais caracteres e termina com o caracter de final de linha. Pode haver até 254 caracteres em uma linha (incluindo o caracter de final de linha). Quando um programa inicia, o sistema operacional automaticamente define quem é a entrada padrão (geralmente o teclado) e quem é a saída padrão (geralmente a tela).

As facilidades de entrada e saída não fazem parte da linguagem C++ . O que existe é uma biblioteca padrão de classes e funções (métodos) para manipular a transferência de dados entre programa e os dispositivos (devices) de saída e entrada padrão: cin, cout, cin.get(), cin.put(), puts(), gets(). Estas classes e funções são declaradas no arquivo <iostream>. Existem funções úteis para conversão e teste de caracteres declaradas no arquivo <ctype.h>.

As funções de entrada e saída operam sobre streams (fluxos) de caracteres. Toda vez que uma função se entrada é chamada (por exemplo, cin, cin.get()) ela verifica pela próxima entrada disponível na entrada padrão (por exemplo, texto digitado no teclado). Cada vez que uma função de saída é chamada, ela entrega o dado para a saída padrão (por exemplo, a tela).

As funções para leitura da entrada padrão e para escrita na saída padrão que têm sido usadas até agora são:

  1. Entrada e saída de caracteres:
    int cin.get( void );
    int cin.put( int );
    

  2. Entrada e saída de strings:
    char *gets(char *);
    int puts( char *);
    

  3. Entrada e saída formatada:
    int cin >> arg1 >> arg2 >> ...;
    int cout << arg1 << arg2 << ...;
    

29.1 Comandos de entrada e saída: cin.get() e cin.put()

Vamos discutir algumas funções de entrada de dados (diferente do cin). A entrada de texto é considerada como um fluxo de caratecteres. Um fluxo texto é uma sequência de caracteres dividida em linhas; cada linha consiste de zero ou mais caracteres seguido do caractere de nova linha ( \n). Como programador, você não quer se preocupar em como as linhas são representadas fora do programa. Quem faz isso por você são funções de uma biblioteca padrão.

Suponha que você queira ler um único caractere, sem fazer qualquer tipo de conversão para um tipo de dados específico (o que é feito por cin). Isso pode ser feito usando a função cin.get().

A função cin.get() lê o caracter do teclado e mostra o que foi digitado na tela.

#include <iostream>
using namespace std;

int main()
{
   char ch;

   cout << "Digite algum caracter: ";

   ch = cin.get();

   cout << "\n A tecla pressionada eh " << ch << endl;

}

O Resultado deste programa na tela é:

   Digite algum caracter: A
    A tecla pressionada eh A.

A função cin.put() aceita um argumento de entrada, cujo valor será impresso como caracter:

#include <iostream>
using namespace std;

int main()
{
   char ch;

   cout << "Digite algum caracter: ";

   ch = cin.get();

   cin.put(ch);

}

29.2 Considerações sobre Leitura de Dados pelo Teclado

29.2.1 Lendo o teclado usando cin.get()

cin.get() é uma função vinculada à primitiva principal de entrada cin. Cada vez que é chamada, esta função lê um caractere teclado; cin.get começa a ler depois que a tecla \enteré digitada no final de uma sequência de caracteres (dizemos que a entrada para a função cin.get() está no fluxo de entrada). A função cin.get() retorna um valor, o caractere lido (mais precisamente, o código inteiro ASCII correspondente ao caractere).

Vejamos o que acontece quando um programa trivial é executado.

#include <iostream>
using namespace std;

int main(){

    char ch;

    ch = cin.get();
}

cin.get() obtém sua entrada do teclado. Portanto, quando o programa acima é executado, o programa espera que o usuário digite alguma coisa. Cada caractere digitado é mostrado no monitor. O usuário pode digitar diversos caracteres na mesma linha, inclusive backspace para corrigir caracteres já digitados. No momento que ele teclar \enter, o primeiro caractere da sequência digitada é o resultado da função cin.get(). Portanto, na instrução do programa acima o caractere (ou melhor, o seu código ASCII) é atribuído a variável ch. Note que o usuário pode ter digitado diversos caracteres antes de teclar \enter, mas a função cin.get() só começará a ler o que foi digitado depois que for teclado \enter. Além disso, com uma chamada da função cin.get() só o primeiro caractere da sequência digitada é lida.

Você deve saber que o caractere de nova linha, \n, que tem o código ASCII 10, é automaticamente adicionado na sequência de caracteres de entrada quando o \enteré teclado. Isso não tem importância quando a função cin.get() é chamada uma única vez, mas isto pode causar problemas quando ele é usado dentro de um laço.

No inicício de qualquer programa que usa cin.get(), você deve incluir

#include <iostream>
using namespace std;

Esta diretiva do pré-processador diz ao compilador para incluir informações sobre cin, cin.get() e EOF (mais sobre EOF adiante.).

Considere o seguinte programa:

   #include <iostream>
   using namespace std;

   int main(){
   
      char ch;
   
         cout <<  "Entre com uma letra: ";
         ch = cin.get();

         if( ch < 'A' || ch > 'z' )
            cout <<  "Voce nao teclou uma letra!";
         else
            cout <<  "Voce teclou " << ch 
                 << ", e seu codigo ASCII eh " << (int) ch << endl;
   }

Um exemplo da execução do programa:

        Entre com uma letra: A
        Voce teclou A, e seu codigo ASCII eh 65.

No exemplo de execução acima o usuário teclou A e depois \enter.

Outro exemplo de execução do programa:

        Entre com uma letra: AbcD
        Voce teclou A, e seu codigo  ASCII eh 65.

Neste caso o usuário digitou quatro caracteres e depois teclou \enter. Embora quatro caracteres tenham sido digitados, somente uma chamada a função cin.get() foi feita pelo programa, portanto só um caractere foi lido. O valor atribuído ao argumento da função é o código ASCII do primeiro caractere lido.

O tipo do resultado da função cin.get() é int e não char. O valor retornado pela função é o código ASCII do caractere lido.

29.2.2 Marcando o final da entrada

Frequentemente quando você está digitando a entrada para o programa, você quer dizer ao programa que você terminou de digitar o que queria. Em ambiente Unix, digitando ^D (segure a tecla de Ctrl e pressione D) você diz ao programa que terminou a entrada do programa. Em ambiente MS-Windows, você faz isto digitando ^Z (segure a tecla de Ctrl e pressione Z).

Isto envia uma indicação para a função cin.get(). Quando isso ocorre, o valor de ch depois de executar ch = cin.get(); será um valor especial do tipo inteiro chamado EOF (que significa end of file - final do arquivo).

Considere o seguinte programa exemplo que conta o número de caracteres digitados (incluindo o caractere de “próxima linha”):

  #include <iostream>
  using namespace std;

  int main()
  {

   int total = 0;
   char ch;

   // Le o proximo caractere em ch e pára quando encontrar
   // final do arquivo 
   ch = cin.get();
   while( ! cin.eof() ) {
      total++;
      ch = cin.get();
   }
   cout << endl <<  total << " caracteres digitados" << endl;
  }

Só para esclarecer: você deve teclar \enterdepois de entrar com o comando ^D (ou ^Z no MS-Windows).

29.2.3 Para evitar problemas com a entrada...

(Observação: nesta seção, espaços em branco são relevantes e são mostrados como \verbspc)

Quando você executa um programa, cada caractere que você digita é lido e considerado como parte do fluxo de entrada. Por exemplo, quando você usa cin.get(), você deve teclar \enterno final. Como mencionado anteriormente, o primeiro caractere digitado é lido pelo cin.get(). Mas, o caractere de nova linha continua no fluxo de entrada (porque você teclou \enter).

De qualquer forma, se você executar um cin.get() depois de um cin ou de um cin.get() você lerá o caractere de nova linha deixado no fluxo de entrada.

Da mesma forma, quando você usa cin para ler informações, ele somente lê o que é necessário. Se voce usar cin para ler um número inteiro e digitar 42\verbspc\verbspc (seguido de \enter), o cin 42, mas deixa \verbspc\verbspc (e o caractere de nova linha do \enter) no fluxo de entrada.

Outro caso “problemático” é quando o cin é usado num laço. Se você digitar um valor do tipo errado, o cin lerá o valor errado e a execução do laço continuará na sentença após o cin . Na próxima iteração do laço o cin vai tentar ler novamente, mas o “lixo” deixado da iteração anterior ainda estará lá, e portanto a chamada corrente do cin também não dará certo. Este comportamento resultará num laço infinito (um laço que nunca termina), ou terminará e terá um resultado errado.

Há uma maneira simples de resolver este problema; toda vez que você usar cin.get() (para ler um caracter só) ou cin , você deve ler todo o “lixo” restante até o caractere de nova linha. Colocando as seguinte linhas após chamadas a cin.get() ou cin o problema é eliminado:

        // Pula o restante da linha
        while( cin.get(c) != '\n' );

Note que isso não é necessário após todas as chamadas a cin.get() ou cin . Só depois daquelas chamadas que precedem cin.get() (ou cin ), especialmente em um laço.

29.3 Validação de entrada

Muitas vezes queremos em nossos programas que eles obtenham um certo tipo de dado do usuário (por exemplo, valores inteiros) mas queremos também tratar a condição em que o usuário digita um dado real ou texto no lugar, exigindo uma reentrada de dados.

O exemplo abaixo nos mostra como fazer isto, usando a sentença com cin como expressão de um if ou while.

Exemplo 1:

#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstring>

using namespace std;

void cleanInput()
{
  char cc;

  if (cin.fail())
  {
    cin.clear();
    while (cin.get(cc) && cc != '\n');
  }
}

void printStatInput()
{
  cout << "\tGood: " << cin.good() 
       << "     Fail: " << cin.fail() 
       << "     Bad: " << cin.bad() 
       << "     EOF: " << cin.eof() << endl << endl;
}


int main ()
{
  int b=1111, c=2222, d=3333;
  float x=1.111, y=2.222;
  void * cinerr;

  cout << "b c d = " << b << " " << c << " " << d << endl;
  cout << "x y = " << x << " " << y << endl;

  while (! (cinerr = cin >> b))
  {
    cleanInput();
    cout << "cin 1 = " << cinerr << " b = " << b << endl;
    printStatInput();
    cout << "Entrada 1: ";
  }
  cout << "** cin 1 = " << cinerr << " b = " << b << endl;
    
  while (! (cinerr = cin >> b >> c >> d))
  {
    cleanInput();
    cout << "cin 2 = " << cinerr << " b c d = " << b << " " << c << " " << d << endl;
    printStatInput();
    cout << "Entrada 2: ";
  }
  cout << "** cin 2 = " << cinerr << " b c d = " << b << " " << c << " " << d << endl;

  cinerr = cin >> x;
  cout << "cin 3 = " << cinerr << " x = " << x << endl;
  printStatInput();

  if (!cinerr)
  {
    /* Limpar condição de erro em cin, se houver.    */
    cout << "Limpando condições de erro de CIN" << endl;
    cin.clear();
    printStatInput();
  }

  cout << "Ultima entrada: ";
  cinerr = cin >> x >> y;
  cout << "cin 4 = " << cinerr << " x y = " << x << " " << y << endl;
  printStatInput();

  return 0;

}

cin » ... retorna um valor especial5. Se nulo, a entrada não foi aceita e o valor da variável em que houve erro fica inalterado. As variáveis posteriores na leitura em que houve erro também mantém seus valores inalterados. Execuções subsequentes de cin somente podem ser feitas após a execução de cin.clear(), para eliminar a condição de erro. Caso isto não seja feito, após o primeiro erro de leitura, todos os cin » ... subsequentes darão erro.

Também podem ocorrer problemas de conversão de tipos em cin. Observe o exemplo abaixo:

        int b;
	float x;

	cin >> b;
	cin >> x;

Neste caso, se usuário digita um float (por exemplo, 3.7) no 1º cin, o 2o.º cin não solicitará dado: 'b' recebe 3, deixando os chars '.4' no buffer de leitura. o 2º  cin, então, armazena direto 0.4 em 'x', não aguardando digitação do usuário. Note que neste caso, cin » ... não acusará erro (isto é, não retorna valor nulo).

29.4 Formatação de saída

Para formatar a saída de dados com cout, (por exemplo, restringir a quantidade de casas decimais mostradas, definir o tamanho máximo em caracteres, ou imprimir uma matriz de valores alinhados adequadamente pelas colunas) usam-se a função setw() e o método setf() (cout.setf(....)).

Os exemplos abaixo mostram como usar estes elementos.

Exemplo 2:

#include <iostream>
#include <iomanip>    // Necessário para usar os manipuladores setw()

  using namespace std;

  int main() {
    int n1, n2, n3;
    int soma;

    cout <<  "Entre com 3 numeros inteiros: ";
    cin >> n1 >> n2 >> n3;
    soma = n1 + n2 + n3;
    cout << "Soma = " << soma << endl;

    /* Define impressão de números em ponto fixo e
       mostra sempre o ponto decimal seguido por zeros
    */
    cout.setf (ios::fixed | ios::showpoint);

    /* Outros flags válidos para setf():
       ios::fixed
       ios:scientific
       ios::showpoint
       ios::right
       ios::left
    */
    
    /* Define quantidade de algarismos a serem exibidos após ponto decimal.
       Em alguns  compiladores, pode indicar a  quantidade de algarismos
       SIGNIFICATIVOS.
    */
    cout.precision(2);

    // setw() fixa a largura do campo de saída.
    // Aplica-se apenas ao próximo item exibido na saída.
    // Necessário incluir <iomanip> (vide início do código)
    cout <<  "Media = " << setw(8) << soma / 3.0 << endl;
    cout <<  "Produto = " << n1 * n2 * n3 << endl;
  }

Exemplo 3:

//  Exemplo de array 2-D - taboada 

#include <iostream>
#include <iomanip>    // Necessário para usar os manipuladores setw()

using namespace std;

#define LIN 10
#define COL 10

int main()
{
  int x;                //  numero da coluna 
  int y;                //  numero da linha  
  int taboada[LIN][COL]; // tabela de taboada 
  
  //  preenche a taboada 
  
  y=0;
  while(y < LIN)
  {
    x=0;
    while(x < COL)
    {
      taboada[y][x] = y*x;
      x = x+1;
    }
    y = y+1;
  }  
  cout << "\n         Taboada de Multiplicacao\n";
  
  //  Imprime o numero das colunas 
  cout << setw(6) << 0;
  x=1;
  while(x < COL)
  {
    cout << setw(3) << x;
    x = x+1;
  }
  cout << endl;
  
  //  Imprime uma "linha horizontal"
  cout << "   ";
  x=0;
  while (x < 3*COL)
  {
    cout << "-";
    x = x+1;
  }
  cout << endl;
  
  //  Imprime as linhas da tablea.
  //  Cada linha a precedida pelo indice de linha e uma barra vertical 
  
  y=0;
  while (y < LIN)
  {
    cout << setw(2) << y << "|";

    x=0;
    while(x < COL)
    {
      cout << setw(3) << taboada[y][x];
      x = x+1;
    }

    cout << endl;
    y = y+1;
}



Notas de rodapé

... especial5
Este valor é um ponteiro para um objeto.

Créditos: Documento produzido pelo Prof. Armando L.N. Delgado (DINF/ET/UFPR), baseado em revisão sobre material de Prof. Carmem Hara e Prof. Wagner Zola (DINF/ET/UFPR).

Esta obra está licenciada com uma Licença Creative Commons Atribuição-NãoComercial-CompartilhaIgual 4.0 Internacional.  Licença Creative Commons

Armando Luiz Nicolini Delgado
2020-10-20