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 efectuar operações de I/O (escrita e/ou leitura). Antes de se poderem efectuar 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 fecho (close).
As operações de I/O em streams são bufferizadas, isto é, existe sempre uma área de memória intermédia (o buffer) para e de onde são efectuadas 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.
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 directamente para um arquivo invocando o comando:
out > file1O símbolo < utiliza-se para redireccionar 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 redireccionamento, podem provir de um arquivo, como se mostra a seguir:
in < file2Pode utilizar-se ainda o símbolo | (pipe), que faz uma ligação directa entre o stdout de um programa e o stdin de um outro programa:
prog1 | prog2A saída de prog1, que normalmente seria escrita no terminal, funciona como entrada de prog2, que normalmente a esperaria vinda do teclado.
Exemplo:
#include <stdio.h>Existem funções semelhantes para leitura e escrita de um carácter noutros streams que não o stdin e stdout:
...
int ch;
...
ch = getchar();
putchar((char) ch);
...
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 % |
Entre o carácter % e o carácter especificador de formato podemos colocar ainda os seguintes indicadores:
printf("%-2.3f\n", 17.23478);produz no terminal a saída: 17.234
printf("VAT = 17.5%%\n");que escreve: VAT = 17.5%
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 excepçã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 directamente 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);
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 aceder. O parâmetro mode controla o tipo de acesso. Se o arquivo especificado não puder ser acedido, por qualquer razão, a função retornará um apontador nulo (NULL).
Os modos principais incluem:
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>É sempre boa política verificar se os arquivos que se pretendem abrir, o foram efectivamente:
...
FILE *stream;
...
stream = fopen("myfile.dat", "rb");
if ( (stream = fopen("myfile.dat", "rb")) == NULL) {
printf("Can't open %s\n", "myfile.dat");
exit(1);
}
int fprintf(FILE *stream, char *format, ...);Exemplo:
int fscanf(FILE *stream, char *format, ...);
#include <stdio.h>Outras funções que trabalham com streams associados a arquivos:
...
char string[80];
FILE *stream;
...
if ( (stream = fopen(...)) != NULL)
fscanf(stream, "%s", string);
...
int fgetc(FILE *stream);As duas funções anteriores são idênticas a getchar() e putchar(), já descritas.
int fputc(char ch, FILE *stream);
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;Os streams pré-definidos e abertos também podem ser acedidos com as funções próprias dos arquivos:
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;
fprintf(stderr, "Cannot compute!!\n");
fscanf(stdin, "%s", string);
int sprintf(char *string, char *format, ...);Por exemplo:
int sscanf(char *string, char *format, ...);
#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);
...
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:
#include <stdio.h>Se este programa for compilado como args.exe e se for invocado como: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]);
}
args f1 f2 "f3 a" 4 stop!a saída será:
argc = 6Notas: argv[0] é o nome do programa; argc conta também o nome do programa; os argumentos são delimitados por espaço, excepto se o argumento for incluído entre aspas; as aspas não fazem parte do argumento.argv[0]: args
argv[1]: f1
argv[2]: f2
argv[3]: f3 a
argv[4]: 4
argv[5]: stop!
2. Escreva um programa, denominado last, que escreve no vídeo as últimas n linhas de um arquivo de texto. Por defeito o valor de n é 5, podendo ser modificado através de um argumento passado na linha de comando (-n). O nome do arquivo de texto é também passado na linha de comando. A sintaxe para invocar este programa é então: last [-n] file_name
3. Escreva um programa que compare 2 arquivos de texto, passados na
linha de comando. Sempre que 2 linhas diferirem, estas deverão ser
escritas no vídeo com a indicação do arquivo a que
pertencem.