Projeto de Algoritmos

"Entrada" e "saída"

Esta página descreve superficialmente as funções "de entrada" (= input) e "de saída" (= output) mais importantes da linguagem C.  Todas estão na biblioteca stdio. Portanto, o seu programa deve ter um

#include <stdio.h>

para usar essas funções.

Teclado e tela

A função  printf (abreviatura de print formatted) exibe na tela do monitor uma lista "formatada" de números, caracters, strings etc.  O primeiro argumento da função é uma string que especifica o formato da impressão.

A função  scanf lê do teclado (= keyboard) e "formata" uma lista de números, caracters, strings etc.  O primeiro argumento da função é uma string que especifica o formato da lista a ser lida. Os demais argumentos são os endereços dos objetos onde os valores lidos serão armazenados.

Exemplo:

#include <stdio.h>

int main (void) {
   int a, b;
   double media;

   scanf ("%d %d", &a, &b);
   media = (a + b) / 2.0;
   printf ("A média de %d e %5d é %7.2f\n", a, b, media);
   return EXIT_SUCCESS;
}

Supondo que o nome do programa é xx, teremos o seguinte resultado (o computador escreve em vermelho e o usuário em azul):

prompt> xx
222 333
A média de 222 e   333 é  277.50
prompt>

Arquivos

Um arquivo (= file) é uma seqüência de bytes que reside em um disco magnético. Abstratamente, um arquivo tem estrutura semelhante à memória do computador. Mas, ao contrário do que acontece com a memória do computador, os bytes de um arquivo não podem ser endereçados individualmente. Assim, o acesso a um arquivo é estritamente seqüencial: para chegar ao 5º byte é preciso passar pelo 1º, 2º, 3º e 4º bytes.

Para manipular um arquivo dentro de um programa, é preciso associar a ele uma variável do tipo FILE *.  Essa operação de associação é conhecida como "abertura" do arquivo e é executada pela função fopen (abreviatura de file open). O primeiro argumento de fopen é o nome do arquivo e o segundo argumento é "r" ou "w" para indicar se o arquivo deve ser aberto "para leitura" ou "para escrita".  Se fopen não encontra o arquivo especificado, devolve o valor NULL.

Depois de usar o arquivo, é preciso "fechá-lo" com a função fclose (abreviatura de file close).

Exemplo: Digamos que o arquivo dados.txt contém uma seqüência de números inteiros separados por brancos. O programa abaixo calcula a média dos números. Para ler o arquivo, o programa usa a função fscanf (abreviatura de file scanf):

#include <stdio.h>
#define TRUE 1

int main (void) {
   int x, n, k;
   double soma;
   FILE *entrada;

   entrada = fopen ("dados.txt", "r");
   if (entrada == NULL) {
      printf ("\nNão encontrei arquivo\n");
      exit (EXIT_FAILURE);
   }
   soma = n = 0;
   while (TRUE) {
      k = fscanf (entrada, "%d", &x);
      if (k != 1) break;
      soma += x;
      n += 1;
   }
   fclose (entrada);
   printf ("A média dos números é %f\n", soma / n);
   return EXIT_SUCCESS;
}

A função fscanf, tal como a função scanf, devolve o número de objetos efetivamente lidos. O programa acima usa isso para detectar o fim do arquivo. A propósito: o programa supõe que o arquivo contém pelo menos um número!

Stdin e stdout

O teclado é um "arquivo padrão de entrada" (= standard input). Ele está permanente "aberto" e é representado pela constante stdin. Portanto fscanf(stdin,...) equivale a scanf(...).

Algo análogo acontece com as funções printf, fprintf e o "arquivo" stdout, que representa a tela do monitor.

Leitura de caracteres

A função mais básica de entrada de dados — mais básica que scanf — é  getc  (o nome é uma abreviatura de get character).  Cada chamada da função lê um caracter de um arquivo.  Se arq aponta um arquivo então getc(arq) lê o próximo caracter do arquivo. Em particular, getc(stdin) lê o próximo caracter do teclado.

A função correspondente de gravação de caracteres é  putc  (o nome é uma abreviatura de put character).  O primeiro argumento da função é o caracter que você deseja gravar e o segundo é o arquivo onde o caracter deve ser gravado. Por exemplo, putc('*', stdout) exibe um  *  na tela do monitor.

Exemplo: O programa abaixo lê uma linha de caracteres do teclado, armazena essa linha em um vetor (= array) e em seguida exibe esses caracteres no monitor. Vamos supor que a linha tem no máximo 100 caracteres (incluindo o '\n' final):

#include <stdio.h>
#define TRUE 1

int main (void) {
   char linha[100];
   int i, j;

   j = 0;
   while (TRUE) {
      linha[j] = getc (stdin);
      if (linha[j] == '\n') break;
      j = j + 1;
   }
   for (i = 0; i <= j; i += 1)
      putc (linha[i], stdout);
   return EXIT_SUCCESS;
}

Outro exemplo: O programa abaixo lê o primeiro caracter do arquivo dados.txt e exibe esse caracter na tela do monitor (o program tem um defeito, que discutiremos a seguir):

#include <stdio.h>

int main (void) {
   char c; /* erro */
   FILE *entrada;

   entrada = fopen ("dados.txt", "r");
   if (entrada == NULL) exit (EXIT_FAILURE);
   c = getc (entrada);
   fclose (entrada);
   putc (c, stdout);
   return EXIT_SUCCESS;
}

Que tipo de objeto getc devolve?

Que acontece se getc tenta ler o próximo caracter de um arquivo que já acabou?  É preciso que getc devolva algum tipo de "caracter inválido". Acontece que todos os 256 caracteres são "válidos"!

Para resolver o impasse, getc não devolve um char mas sim um int,  pois o conjunto de valores de int contém o conjunto de valores de char e é maior que esse último.  Se o arquivo tiver acabado, getc devolve um int que não possa ser confundido com um char.  Mais especificamente, a função faz o seguinte:

Se houver um próximo caracter no arquivo, getc Caso contrário (ou seja, se o arquivo não tiver mais caracteres), getc devolve  –1.

Para ser mais exato, se o arquivo não tem mais caracteres a função devolve a constante  EOF  (abreviatura de end of file), que está definida no arquivo-interface stdio.h.  Em muitos computadores — mas não em todos! — o valor de EOF é –1.

A solução adotado por getc é uma boa lição de projeto de algoritmos.  Situações análogas acontecem em muitas outras ocasiões: a função devolve um objeto que pertence a um superconjunto do conjunto em que estamos realmente interessados.

Exemplo: O seguinte fragmento de código exibe o próximo caracter do arquivo a menos que estejamos no fim do arquivo:

   int i;
   i = getc (entrada);
   if (i != EOF) 
      printf ("%c", i);
   else 
      printf ("\nO arquivo terminou!");

Se o arquivo for stdin (ou seja, o teclado) o sinal de fim-de-arquivo é produzido pela combinação de teclas Ctrl-D.

Exercícios

  1. Qual o efeito do seguinte trecho de código?
       unsigned char c; 
       while ((c = getc (entrada)) != EOF)
          printf ("%c ", c);
    

  2. Suponha que o arquivo dados.txt contém a cadeia de caracteres "ABCÿDEF" e nada mais. O que o seguinte programa imprime?
    int main (void) {
       FILE *entrada;
       int c; 
       entrada = fopen ("dados.txt", "r");
       while ((c = getc (entrada)) != EOF)
          printf ("%c ", c);
       fclose (entrada);
       return EXIT_SUCCESS;
    }
    

  3. Escreva um programa completo que faça uma cópia de um arquivo. O nome do arquivo é digitado pelo usuário.  [Solução]

  4. Escreva um programa que remova os comentários de um programa C padrão ANSI. O programa original está gravado em um arquivo; o programa "limpo" deve ser gravado em outro arquivo.

Mais exercícios

  1. Escreva um programa que conta o número de ocorrências de cada caracter em um arquivo. O programa solicita o nome do arquivo ao usuário e imprime uma tabela que dá o número de ocorrências de cada caracter.

    Para ganhar inspiração, analise o comportamento do utilitário wc (abreviatura de word count).

 


URL of this site: www.ime.usp.br/~pf/algoritmos/
1998 | Last modified: Sat Sep 16 10:11:52 BRT 2006
Paulo Feofiloff
IME-USP