Este capítulo descreve, superficialmente,
as funções
de entrada
(= input) e
de saída
(= output) mais importantes
da linguagem C.
Todas estão na biblioteca
stdio.
Para ter acesso a essa biblioteca,
seu programa deve incluir a interface
da biblioteca por meio de
#include <stdio.h>
Sumário:
A função printf
(abreviatura de print formatted)
exibe na tela do terminal
uma lista formatada
de
números,
caracteres,
strings,
etc.
O primeiro argumento da função
é uma string
que
especifica o formato da impressão.
A função scanf (abreviatura de scan formatted) recebe do teclado uma lista de números, caracteres, strings, etc. O primeiro argumento da função é uma string que especifica o formato da lista. Os demais argumentos são os endereços das variáveis onde os valores recebidos devem ser armazenados. A função trata todos os brancos como se fossem espaços (caracteres ' '). Veja um exemplo:
#include <stdio.h> #include <stdlib.h> int main (void) { int a, b; scanf ("%d %d", &a, &b); double media; media = (a + b)/2.0; printf ("A média de %d e %d é %f\n", a, b, media); return EXIT_SUCCESS; }
Se o nome do programa for average, veremos o seguinte resultado na tela (o computador escreve em vermelho):
~$ ./average 222 333 A média de 222 e 333 é 277.500000 ~$
Para exibir o caractere ASCII representado por um char ou unsigned char, use a função printf com especificação de formato %c. Por exemplo, o fragmento de programa
char a = 65; // equivalente: a = 'A';
printf ("%d %c", a, a);
exibe
65 A
Para ler um caractere ASCII do teclado, use a função scanf com especificação de formato %c. A função lê o primeiro caractere não-branco (os brancos anteriores são desprezados), converte o caractere lido no correspondente byte, e armazena o byte em a:
char a; scanf ("%c", &a);
Para ler uma cadeia de caracteres do teclado, use scanf com especificação de formato %s. A função interrompe a leitura ao encontrar o primeiro caractere branco. (Para ler uma cadeia de caracteres que contém brancos, use a função fgets.) Por exemplo, o fragmento de programa abaixo lê uma cadeia de caracteres, converte a cadeia numa string e armazena a string no vetor str:
char str[100]; scanf ("%s", str);
Um arquivo
(= file)
é uma sequência de bytes
que reside na memória lenta
do computador
(um disco magnético, por exemplo).
Os bytes de um arquivo
não têm endereços individuais.
Assim, o acesso ao arquivo é estritamente sequencial:
para chegar ao 5º byte é
preciso passar pelo 1º, 2º, 3º e 4º bytes.
Para que um programa possa manipular um arquivo,
é preciso associá-lo a uma variável do
tipo FILE
(esse tipo está definido
na interface stdio.h).
A operação de associação é conhecida como
abertura do arquivo
e é executada pela função fopen
(= file open).
O primeiro argumento da função é o nome do arquivo
e o segundo argumento é "r" ou "w"
para indicar se o arquivo deve ser aberto
para leitura
(= read)
ou para escrita
(= write).
A função fopen devolve o endereço de um FILE
(ou NULL,
se não encontrar o arquivo especificado).
Depois de usar o arquivo, é preciso fechá-lo
com a função fclose
(= file close).
Digamos, por exemplo, que o arquivo de texto dados.txt contém uma sequência de números inteiros (em notação decimal) separados por brancos. O programa abaixo calcula a média desses números. Para ler o arquivo, o programa usa a função fscanf (o nome é uma abreviatura de file scanf), que generaliza a função scanf:
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> int main (void) { FILE *entrada; entrada = fopen ("dados.txt", "r"); if (entrada == NULL) { printf ("\nNão encontrei o arquivo!\n"); exit (EXIT_FAILURE); } double soma = 0.0; int n = 0; while (true) { int x; int 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. Se não houver um número a ser lido, devolve 0. O programa acima usa isso para detectar o fim do arquivo. (O programa supõe que o arquivo contém pelo menos um número.)
Stdin e stdout.
O arquivo
padrão de entrada
(= standard input)
é o teclado.
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
padrão de saída stdout,
que corresponde à tela do terminal.
A função mais básica de saída — mais básica que fprintf — é putc (o nome é uma abreviatura de put character). A função recebe um byte e grava-o no arquivo especificado. (Muitas vezes, o byte tem valor entre 0 e 127 e portanto representa um caractere ASCII.) Se c é um char e arq é um FILE então putc (c, arq) grava c em arq. Por exemplo, putc ('?', stdout) exibe o caractere ? na tela.
A correspondente função de entrada é getc (o nome é uma abreviatura de get character). Cada chamada da função lê um byte do arquivo especificado. (Muitas vezes, o byte lido representa um caractere ASCII.) Por exemplo, getc (stdin) lê o próximo byte do teclado e devolve o byte lido.
As expressões putchar (x) e getchar () são abreviaturas de putc (x, stdout) e getc (stdin) respectivamente.
Exemplo. O programa abaixo lê do teclado uma sequência de bytes que termina com \n (como uma linha de caracteres ASCII, por exemplo), armazena essa sequência em um vetor, e exibe os correspondentes caracteres na tela. O programa supõe que a sequência tem no máximo 100 bytes (incluindo o \n final):
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> int main (void) { char linha[100]; int n = 0; while (true) { linha[n] = getchar (); if (linha[n] == '\n') break; n = n + 1; } for (int i = 0; i <= n; i += 1) putchar (linha[i]); return EXIT_SUCCESS; }
Outro exemplo. O programa abaixo deveria ler o primeiro byte do arquivo dados.txt e exibir o correspondente caractere (supõe-se que seja um caractere ASCII) na tela:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
FILE *entrada;
entrada = fopen ("dados.txt", "r");
if (entrada == NULL) exit (EXIT_FAILURE);
char c; // erro!
c = getc (stdin);
fclose (entrada);
putc (c, stdout);
return EXIT_SUCCESS;
}
O programa ter um defeito a ser discutido na próxima seção.
Que acontece se getc tenta ler o próximo byte
de um arquivo que já se esgotou?
Seria preciso que getc devolvesse algum tipo de
byte inválido
.
Mas todos os 256 bytes são válidos!
Para resolver esse impasse, a função getc sempre devolve um int, não um byte. O conjunto de valores int contém todos os valores char e mais alguns. Assim, se o arquivo tiver se esgotado, getc pode devolver um int distinto de qualquer char. Mais especificamente,
Para ser mais exato, se o arquivo não tiver mais bytes, getc devolve a constante EOF (o nome é uma abreviatura de end of file), que está definida na interface stdio.h e vale −1 na maioria dos computadores.
Resumindo, a função getc devolve um elemento de um superconjunto do conjunto em que estamos realmente interessados. Assim, a resposta de getc é sempre do mesmo tipo (um int), até em situações excepcionais. Esse truque é uma importante lição de projeto (design) de programas.
Exemplo. O seguinte fragmento de código exibe o próximo byte do arquivo a menos que estejamos no fim do arquivo:
int c; c = getc (entrada); if (c != EOF) putc (c, stdout); else printf ("\nO arquivo terminou!");
(A propósito, se o arquivo entrada for stdin, o fim do arquivo é produzido pela combinação de teclas Ctrl D, que gera o byte 4.)
arquivo acabouna tela. Critique essa proposta.
A função fputs (o nome é uma abreviatura de file put string) da biblioteca stdio grava uma string num arquivo. Por exemplo,
fputs (str, arq);
grava a string str (exceto seu byte nulo final) no arquivo arq. (O efeito é essencialmente o mesmo de fprintf (arq, "%s", str).)
Se o segundo argumento de fputs for stdout, a string str é exibida na tela do terminal depois de ser convertida em uma cadeia de caracteres.
A função fgets (o nome é uma abreviatura de file get string) da biblioteca stdio lê uma linha de um arquivo e armazena a linha em uma string. Por exemplo, se str é um vetor de bytes então
fgets (str, 100, arq);
armazena em str a linha corrente do arquivo arq (inclusive o \n que marca o fim da linha) e acrescenta um byte nulo final.
O segundo argumento de fgets é uma proteção contra linhas muito longas, mais longas que o espaço alocado para o primeiro argumento. No exemplo acima, se o número de bytes da linha corrente do arquivo (incluído o \n final) for maior que 99, somente os 99 primeiros bytes serão armazenados em str.
Se o último argumento de fgets for stdin, a função lê uma cadeia de caracteres do teclado até o fim da linha (tecla ↵), converte a cadeia numa string, e armazena a string no endereço indicado pelo primeiro agumento.
A execução de qualquer programa C consiste na execução da função main (que em geral invoca outras funções). A função main admite dois parâmetros, que chamaremos numargs e arg. O segundo parâmetro é um vetor de strings e o primeiro é o número de elementos do vetor.
int main (int numargs, char *arg[]) { . . . }
Os argumentos são digitadas pelo usuário no teclado ao invocar o programa. (O usuário digita um ou mais espaços para separar cada argumento do anterior.) A primeira string digitada é o nome do programa e passará a ser o valor de arg[0]. As strings seguintes são conhecidas como argumentos na linha de comando (= command line arguments) e passarão a ser os valores de arg[1], arg[2], … , arg[numargs-1]. O valor de numargs é definido implicitamente pelo número de strings digitadas.
No seguinte exemplo, estamos supondo que o nome do programa é prog. Depois que o usuário digita a linha de comando
~$ ./prog -a bb ccc 2222
o valor de numargs será 5 e os valores de arg[0] a arg[4] serão as strings "prog", "-a", "bb", "ccc" e "2222" respectivamente.
Exemplo. O seguinte programa calcula a média dos números inteiros digitados na linha de comando. O usuário precisa garantir que cada um dos números digitados esteja no intervalo INT_MIN..INT_MAX.
#include <stdio.h> #include <stdlib.h> int main (int numargs, char *arg[]) { int soma = 0; for (int i = 1; i < numargs; ++i) soma += strtol (arg[i], NULL, 10); int n = numargs - 1; printf ("média = %.2f\n", (double) soma / n); return EXIT_SUCCESS; }
Se o nome do programa é prog, veremos a seguinte interação na tela:
~$ ./prog +22 33 -11 +44 média = 22.00 ~$
Outro exemplo. O seguinte programa exibe uma tabela de conversão de graus Celsius em graus Fahrenheit ou vice-versa. O usuário especifica a direção da conversão, bem como o início e o fim da tabela.
#include <stdio.h> #include <stdlib.h> #include <string.h> // Programa temperatura // -------------------- // Para obter uma tabela de conversão de graus // Celsius em graus Fahrenheit, digite // // ./temperatura c-f 10 40 // // A primeira coluna começará com 10 graus // Celsius e andará em passos de 1 grau até 40 // graus Celsius. A segunda coluna trará a // correspondente temperatura em graus // Fahrenheit. Troque "c-f" por "f-c" para // obter a tabela de conversão de graus // Fahrenheit em graus Celsius. int main (int numargs, char *arg[]) { if (numargs != 4) { printf ("Número de argumentos errado.\n"); return EXIT_FAILURE; } int inf = strtol (arg[2], NULL, 10); int sup = strtol (arg[3], NULL, 10); // arg[2] e arg[3] devem representar inteiros // no intervalo INT_MIN..INT_MAX if (strcmp (arg[1], "c-f") == 0) { printf ("Celsius Fahrenheit\n"); for (int c = inf; c <= sup; c += 1) printf ("%7d %10.2f\n", c, 9.0/5.0*c + 32); return EXIT_SUCCESS; } if (strcmp (arg[1], "f-c") == 0) { printf ("Fahrenheit Celsius\n"); for (int f = inf; f <= sup; f += 1) printf ("%10d %8.2f\n", f, 5.0*(f-32)/9.0); return EXIT_SUCCESS; } return EXIT_FAILURE; }
Redirecionamento de entrada/saída.
Se um argumento na linha de comando for o nome de um arquivo
precedido pelo caractere <
então esse arquivo passa a fazer o papel da
entrada padrão.
Analogamente,
se um argumento na linha de comando for o nome de um arquivo
precedido pelo caractere >
então esse arquivo passa a fazer o papel da
saída padrão.
Por exemplo, se o arquivo in.txt
no diretório corrente contém
222 333
e average é o nome do programa no primeiro exemplo deste capítulo, então a invocação
~$ ./average < in.txt > out.txt
produzirá um arquivo out.txt com o seguinte conteúdo:
A média de 222 e 333 é 277.500000
Resposta: Provavelmente o arquivo foi gerado no sistema Windows e está sendo exibido no sistema Linux. No sistema Linux, o fim de uma linha é indicado pelo caractere \n. Já no sistema Windows, o fim de uma linha é indicado pelo par de caracteres \r\n, e o caractere \r aparece na tela como ^M. Essa diferença entre os dois sistemas é uma fonte de dor de cabeça para quem precisa processar no Linux arquivos que vieram do Windows: é preciso lidar com os caracteres \r que aparecem no fim das linhas.