Capítulo 11 - Entrada e Saída


Conteúdo

Capítulo anterior

Capítulo seguinte

Este capítulo focará algumas das muitas formas de Entrada/Saída (I/O) da Linguagem C. Algumas dessas formas foram já brevemente mencionadas em capítulos anteriores, mas tornaremos a estudá-las, com muito mais detalhe aqui. Praticamente todas as formas de I/O do C têm os seus tipos e funções declarados no arquivo de inclusão da biblioteca standard stdio.h.


Tópicos



Streams

Os streams constituem uma forma portável de ler e escrever informação nos programas escritos em C. São também uma forma bastante eficiente e flexível de efetuar essa transferência de informação.

Um stream é um arquivo ou outro dispositivo de entrada/saída (por exemplo: o terminal, o teclado, a impressora, etc) que é manipulado através de um apontador para o stream. Na biblioteca standard os streams são representados por uma estrutura definida em stdio.h e com o nome de FILE. Os programas que manipulam streams usam então um apontador para esta estrutura (FILE *).

Do ponto de vista do programa em C não é necessário conhecer mais nada acerca desta estrutura. Basta declarar um apontador para ela e usá-lo para efetuar operações de I/O (escrita e/ou leitura). Antes de se poderem efetuar propriamente as operações de transferência de informação para os streams é necessário executar uma operação de abertura (open). Quando já não houver mais necessidade de novas operações de transferência de informação podemos (e devemos) fechar o stream com uma operação de fechamento (close).

As operações de I/O em streams são bufferizadas, isto é, existe sempre uma área de memória intermediaria (o buffer) para e de onde são efetuadas as transferências de informação para os dispositivos que os streams representam. Na ilustração seguinte pode ver-se um esquema deste mecanismo.

A existência de um buffer leva a uma maior eficiência nas operações de I/O, no entanto os dados presentes no buffer não são imediatamente transferidos para o stream. Qualquer terminação anormal de um programa pode conduzir a perda de informação se esta ainda não tiver sido transferida.

Streams pré-definidos

No UNIX, e na maior parte de outros Sistemas Operacionais temos por default pelo menos 3 streams, já abertos e prontos a serem usados:

Todos eles usam texto como modo de I/O.

Redirecionamento

É um processo de associar os streams pré-definidos stdin e stdout com arquivos ou outros dispositivos de I/O que não o terminal e o teclado. O redirecionamento não faz parte da linguagem C mas sim do sistema operacional. Em geral é efetuado a partir da linha de comando (p. ex. no UNIX).

O símbolo > é utilizado para redireccionar o stdout para um arquivo. Assim se tivermos um programa out que escreve normalmente no vídeo do terminal,  podemos dirigir a sua saída diretamente para um arquivo invocando o comando:

out > file1

O símbolo < utiliza-se para redirecionar um arquivo especificado para o stream pré-definido stdin de um programa. Assim, se tivermos um programa denominado in que espera dados provenientes do teclado, estes, através do redirecionamento, podem provir de um arquivo, como se mostra a seguir:

in < file2

Pode utilizar-se ainda o símbolo | (pipe), que faz uma ligação direta entre o stdout de um programa e o stdin de um outro programa:

prog1 | prog2

A saída de prog1, que normalmente seria escrita no terminal, funciona como entrada de prog2, que normalmente a esperaria vinda do teclado.
 

Up


Operações básicas de entrada/saída

As funções mais básicas que permitem efetuar operações de entrada/saída nos streams pré-definidos stdin e stdout são:

Estas funções, bem como todas as outras que dizem respeito a operações de entrada/saída suportadas pela biblioteca standard do C, estão declaradas no arquivo de inclusão stdio.h.

Exemplo:

#include <stdio.h>
...
int ch;
...
ch = getchar();
putchar((char) ch);
...

Existem funções semelhantes para leitura e escrita de um carácter em outros streams que não o stdin e stdout:

Up


Entrada/saída formatadas

Já temos visto, em exemplos anteriores, a escrita de texto e valores, com um formato especificado, no terminal utilizando a função printf(). Vamos ver a utilização desta função em maior detalhe a seguir. Veremos também a função inversa, que lê valores do teclado diretamente para variáveis - a função scanf().

Printf

A função, também declarada em stdio.h, é definida como se mostra a seguir:

int printf(char *format, ...); - Escreve em stdout a sua lista de argumentos (especificada em ...) de acordo com um formato especificado na string format; retorna o número de caracteres escrito.

A string format contém 2 tipos de especificações:

Especificador de formato
(a seguir a %)

Tipo

Resultado

c

char

um único carácter

i ou d

int

número inteiro

o

int

número em octal

x ou X

int

número em hexadecimal
(com letras minúsculas ou maiúsculas)

u

unsigned int

número inteiro sem sinal

s

char *

escreve o string terminado com \0

f

double ou float

número real com parte decimal

e ou E

double ou float

 número real escrito em notação científica 

g ou G

double ou float

equivalente a e ou f
 (é escolhido o que ocupar menos espaço) 

hi ou hd

short

número inteiro curto

li ou ld

long

número inteiro longo

Lf

long double

número real com parte decimal

%

-

escreve o carácter %

 Em geral os prefixos h e l representam os modificadores short e long, respectivamente.

Entre o carácter % e o carácter especificador de formato podemos colocar ainda os seguintes indicadores:

Alguns exemplos:

printf("%-2.3f\n", 17.23478);

produz no terminal a saída:    17.234

printf("VAT = 17.5%%\n");

que escreve:   VAT = 17.5%

Scanf

A função scanf() é definida em stdio.h como se segue:

int scanf(char *format, ...); - lê caracteres de stdin e coloca os valores lidos e convertidos nas variáveis cujos endereços são passados na lista de argumentos a seguir à indicação do formato; retorna o número de caracteres lidos e convertidos.

A indicação do formato é muito semelhante à especificada para printf(). A única exceção diz respeito aos especificadores f, e ou g, que aqui se referem exclusivamente a valores do tipo float ( os valores lidos e convertidos deverão ser passados a apontadores para variáveis do tipo float). Para especificar valores do tipo double deverão ser usados os especificadores lf, le ou lg.

Como já se disse a lista de argumentos, que irão receber os valores lidos, deverá conter apenas apontadores ou endereços de variáveis. Por exemplo:

scanf("%d", &i);

para ler um valor inteiro do teclado e colocá-lo na variável de tipo int i.

No caso de arrays (strings, por exemplo) o próprio nome pode ser diretamente usado na lista de argumentos, uma vez que representa o endereço da 1ª posição do array. Veja-se o exemplo:

char str[80];
...
scanf("%s", str);

Up


Arquivos

Os streams mais comuns são os que ficam associados a arquivos armazenados em disco. A primeira operação necessária para trabalhar com esse tipo de streams é uma operação de abertura, efetuada com a função fopen():

FILE *fopen(char *name, char *mode);

A função fopen() retorna um apontador para uma estrutura FILE. O parâmetro name é o nome do arquivo armazenado no disco que se pretende acessar. O parâmetro mode controla o tipo de acesso. Se o arquivo especificado não puder ser acessado, por qualquer razão, a função retornará um apontador nulo (NULL).

Os modos principais incluem:

É possível acrescentar aos designadores de modo as letras 't' ou 'b', que especificam o tipo de informação do arquivo: texto ou binária, respectivamente.

O apontador retornado pela função deve ser guardado, uma vez que é necessário como parâmetro para todas as funções de acesso ao stream assim aberto.

Exemplo de abertura de um arquivo chamado myfile.dat para leitura apenas:

#include <stdio.h>
...
FILE *stream;
...
stream = fopen("myfile.dat", "rb");

É sempre boa política verificar se os arquivos que se pretendem abrir, o foram efetivamente:

if ( (stream = fopen("myfile.dat", "rb")) == NULL) {
   printf("Can't open %s\n", "myfile.dat");
   exit(1);
}

Leitura e escrita de arquivos

As funções fprintf() e fscanf() são utilizadas para acessar a streams associados a arquivos de um modo idêntico às funções printf() e scanf(), que estão associadas à saída standard (stdout) e entrada standard (stdin) respectivamente. Incluem apenas mais um parâmetro que é o apontador para FILE obtido com a função fopen():

int fprintf(FILE *stream, char *format, ...);
int fscanf(FILE *stream, char *format, ...);

Exemplo:

#include <stdio.h>
...
char string[80];
FILE *stream;
...
if ( (stream = fopen(...)) != NULL)
   fscanf(stream, "%s", string);
...

Outras funções que trabalham com streams associados a arquivos:

int fgetc(FILE *stream);
int fputc(char ch, FILE *stream);

As duas funções anteriores são idênticas a getchar() e putchar(), já descritas.

size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream); - lê do stream para o array apontado por ptr um número de bytes igual a size*nobj; retorna o número de objectos lidos, que pode ser menor do que nobj;
size_t fwrite(void *ptr, size_t size, size_t nobj, FILE *stream); - escreve no stream provenientes do array apontado por ptr um número de bytes igual a size*nobj; retorna o número de objectos escritos, que pode ser menor do que nobj;
int fflush(FILE *stream); - transfere qualquer informação que porventura ainda se encontre no buffer associado ao stream para o respectivo arquivo;
int fclose(FILE *stream); - fecha o stream desassociando-o do arquivo;

Os streams pré-definidos e abertos também podem ser acessados com as funções próprias dos arquivos:

fprintf(stderr, "Cannot compute!!\n");
fscanf(stdin, "%s", string);

Up


As funções sprintf e sscanf

Estas funções são em tudo idênticas a fprintf() e fscanf(), exceto no fato de escreverem ou lerem para ou de strings em vez de arquivos:

int sprintf(char *string, char *format, ...);
int sscanf(char *string, char *format, ...);

Por exemplo:

#include <stdio.h>
...
float full_tank = 47.0;
float miles = 300;
char miles_per_litre[80];
...
sprintf(miles_per_litre, "Miles per litre = %2.3f", miles / full_tank);
...

Up


Passagem de parâmetros para programas

A linguagem C permite a passagem de argumentos, especificados na linha de comando que chama um programa, para a função que começa a execução do programa. Estes argumentos aparecem na linha de comando logo após o nome do programa e são separados por um espaço. São passados à função main() como strings, através de um mecanismo especial.

Para receber estes argumentos a função main() tem de ser declarada como:

main(int argc, char **argv)

Os parâmetros argc e argv têm o seguinte significado:

Um programa simples que utiliza este mecanismo é:

#include <stdio.h>
void main(int argc, char **argv)
{
   /* programa que imprime os seus argumentos */
   int i;
   printf("argc = %d\n\n", argc);
   for (i=0; i<argc; ++i)
      printf("argv[%d]: %s\n", i, argv[i]);
}

Se este programa for compilado como args.exe e se for chamado como:

args f1 f2 "f3 a" 4 stop!

a saída será:

argc = 6
argv[0]: args
argv[1]: f1
argv[2]: f2
argv[3]: f3 a
argv[4]: 4
argv[5]: stop!

Notas: argv[0] é o nome do programa; argc conta também o nome do programa; os argumentos são delimitados por espaço, exceto se o argumento for incluído entre aspas; as aspas não fazem parte do argumento.
 

Up


[REV 5/2000]