Diferenças
Aqui você vê as diferenças entre duas revisões dessa página.
Ambos lados da revisão anterior Revisão anterior Próxima revisão | Revisão anterior | ||
c:acesso_a_arquivos [2023/08/01 20:10] – edição externa 127.0.0.1 | c:acesso_a_arquivos [2023/08/15 14:50] (atual) – [Leitura formatada] maziero | ||
---|---|---|---|
Linha 1: | Linha 1: | ||
+ | ====== Acesso a arquivos ====== | ||
+ | |||
+ | {{ progc_arquivos.mkv |Video desta aula}} | ||
+ | |||
+ | Aqui serão descritas algumas das funções mais usuais para operações de entrada/ | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | </ | ||
+ | |||
+ | ===== Conceitos básicos ===== | ||
+ | |||
+ | ==== Tipos de arquivos ==== | ||
+ | |||
+ | Um arquivo armazena uma sequência de bytes, cuja interpretação fica a cargo da aplicação. Contudo, para facilitar a manipulação de arquivos, a linguagem C considera dois tipos de arquivos: | ||
+ | |||
+ | * **arquivos de texto**: contém sequências de bytes representando caracteres de texto, separadas por caracteres de controle como '' | ||
+ | * **arquivos binários**: | ||
+ | |||
+ | ==== Streams e descritores ==== | ||
+ | |||
+ | As operações sobre arquivos em C podem ser feitas de duas formas: | ||
+ | |||
+ | * por **streams**: | ||
+ | * por **descritores**: | ||
+ | | ||
+ | Na sequência deste texto serão apresentadas as funções de acesso usando // | ||
+ | |||
+ | ==== Entradas e saídas padrão ==== | ||
+ | |||
+ | Cada programa em execução tem acesso a três arquivos padrão definidos no arquivo de cabeçalho '' | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | Esses três arquivos não precisam ser abertos, eles estão prontos para uso quando o programa inicia. Geralmente eles estão associados ao terminal onde o programa foi lançado, mas podem ser redirecionados pelo //shell// ([[unix: | ||
+ | |||
+ | ===== Abrindo e fechando arquivos ===== | ||
+ | |||
+ | Antes de ser usado, um arquivo precisa ser " | ||
+ | |||
+ | <code c> | ||
+ | FILE* fopen (const char *filename, const char *mode) | ||
+ | </ | ||
+ | |||
+ | Abre um arquivo indicado por '' | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | <note important> | ||
+ | Os modos '' | ||
+ | </ | ||
+ | |||
+ | Fecha um //stream//. Os dados de saída em //buffer// são escritos, enquanto dados de entrada são descartados: | ||
+ | |||
+ | <code c> | ||
+ | int fclose (FILE* stream) | ||
+ | </ | ||
+ | |||
+ | Fecha e abre novamente um //stream//, permitindo alterar o arquivo e/ou modo de abertura: | ||
+ | |||
+ | <code c> | ||
+ | FILE* freopen (const char *filename, const char *mode, FILE *stream) | ||
+ | </ | ||
+ | |||
+ | Exemplo: abrindo o arquivo '' | ||
+ | |||
+ | <code c fopen-read.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | FILE* arq ; | ||
+ | | ||
+ | arq = fopen (" | ||
+ | | ||
+ | if ( ! arq ) | ||
+ | { | ||
+ | perror ("Erro ao abrir arquivo x") ; | ||
+ | exit (1) ; // encerra o programa com status 1 | ||
+ | } | ||
+ | | ||
+ | | ||
+ | exit (0) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Exemplo: abre o arquivo '' | ||
+ | |||
+ | <code c fopen-write.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | FILE* arq ; | ||
+ | |||
+ | arq = fopen (" | ||
+ | |||
+ | if ( ! arq ) | ||
+ | { | ||
+ | perror ("Erro ao abrir/criar arquivo x") ; | ||
+ | exit (1) ; // encerra o programa com status 1 | ||
+ | } | ||
+ | |||
+ | | ||
+ | exit (0) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Arquivos-texto ===== | ||
+ | |||
+ | Arquivos-texto contêm sequências de bytes representando um texto simples (sem formatações especiais, como negrito, itálico, etc), como código-fonte ou uma página em HTML, por exemplo. | ||
+ | |||
+ | Em um arquivo-texto, | ||
+ | |||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | Além dos caracteres em si, as codificações geralmente suportam **caracteres de controle**, | ||
+ | |||
+ | ^ nome ^ valor ^ representação ^ | ||
+ | | null | 0 | '' | ||
+ | | bell | 7 | '' | ||
+ | | backspace | 8 | '' | ||
+ | | tab | 9 | '' | ||
+ | | line feed | 10 | '' | ||
+ | | form feed | 12 | '' | ||
+ | | carriage return | 13 | '' | ||
+ | | escape | 27 | '' | ||
+ | |||
+ | ===== Escrita de arquivos ===== | ||
+ | |||
+ | ==== Escrita simples ==== | ||
+ | |||
+ | Estas funções permitem gravar caracteres ou strings simples em // | ||
+ | |||
+ | <code c> | ||
+ | int fputc (int c, FILE* stream) | ||
+ | int putc (int c, FILE* stream) | ||
+ | int putchar (int c) // idem, em " | ||
+ | |||
+ | int fputs (const char *s, FILE* stream) | ||
+ | int puts (const char *s) // idem, em " | ||
+ | </ | ||
+ | |||
+ | Um exemplo de uso de operações de escrita simples em arquivo: | ||
+ | |||
+ | <code c escreve-ascii.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | FILE *arq ; | ||
+ | unsigned char c ; | ||
+ | |||
+ | // abre o arquivo em escrita | ||
+ | arq = fopen (" | ||
+ | if ( ! arq ) | ||
+ | { | ||
+ | | ||
+ | exit (1) ; | ||
+ | } | ||
+ | |||
+ | // escreve os caracteres ascii | ||
+ | fputs (" | ||
+ | for (c = 32; c < 128; c++) | ||
+ | { | ||
+ | fputc (c, arq) ; | ||
+ | fputc (' ', arq) ; | ||
+ | } | ||
+ | fputc (' | ||
+ | |||
+ | // fecha o arquivo | ||
+ | fclose (arq) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Escrita formatada ==== | ||
+ | |||
+ | As operações de entrada e saída formatada usam padrões para formatação dos diversos tipos de dados descritos em livros de programação em C e no manual da GLibC. | ||
+ | |||
+ | Escreve dados usando a formatação definida em '' | ||
+ | |||
+ | <code c> | ||
+ | int printf (const char* format, ...) | ||
+ | </ | ||
+ | |||
+ | Idêntico a '' | ||
+ | |||
+ | <code c> | ||
+ | int fprintf (FILE* stream, const char* format, ...) | ||
+ | </ | ||
+ | |||
+ | Similar a '' | ||
+ | |||
+ | <code c> | ||
+ | int sprintf (char* str, const char* format, ...) | ||
+ | </ | ||
+ | |||
+ | <note important> | ||
+ | Atenção: o programador deve garantir que '' | ||
+ | </ | ||
+ | |||
+ | <code c escreve-tabuada.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | FILE *arq ; | ||
+ | int i, j ; | ||
+ | |||
+ | // abre o arquivo em escrita | ||
+ | arq = fopen (" | ||
+ | if ( ! arq ) | ||
+ | { | ||
+ | | ||
+ | exit (1) ; | ||
+ | } | ||
+ | |||
+ | // escreve o cabeçalho | ||
+ | fprintf (arq, " | ||
+ | |||
+ | fprintf (arq, " | ||
+ | for (j = 0; j <= 10; j++) | ||
+ | fprintf (arq, " | ||
+ | fprintf (arq, " | ||
+ | |||
+ | fprintf (arq, " | ||
+ | for (j = 0; j <= 10; j++) | ||
+ | fprintf (arq, " | ||
+ | fprintf (arq, " | ||
+ | |||
+ | // escreve as linhas de valores | ||
+ | for (i = 0; i <= 10; i++) | ||
+ | { | ||
+ | fprintf (arq, "%4i | ", i) ; | ||
+ | for (j = 0; j <= 10; j++) | ||
+ | fprintf (arq, " | ||
+ | fprintf (arq, " | ||
+ | } | ||
+ | |||
+ | // fecha o arquivo | ||
+ | fclose (arq) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Leitura de arquivos ===== | ||
+ | |||
+ | ==== Leitura simples ==== | ||
+ | |||
+ | Estas funções permitem ler caracteres isolados de um //stream//. O valor lido é um '' | ||
+ | |||
+ | <code c> | ||
+ | int fgetc (FILE* stream) | ||
+ | int getc (FILE* stream) | ||
+ | int getchar () // Idem, sobre stdin | ||
+ | </ | ||
+ | |||
+ | Para a leitura de strings :!:: | ||
+ | |||
+ | <code c> | ||
+ | char* gets (char *s) | ||
+ | </ | ||
+ | |||
+ | Lê caracteres de '' | ||
+ | |||
+ | <note warning> | ||
+ | Atenção: a função '' | ||
+ | </ | ||
+ | |||
+ | Lê uma linha de caracteres do //stream// e a deposita na string '' | ||
+ | |||
+ | <code c> | ||
+ | char* fgets (char *s, int count, FILE *stream) | ||
+ | </ | ||
+ | |||
+ | O exemplo a seguir lê e numera as 10 primeiras linhas de um arquivo: | ||
+ | |||
+ | <code c numera-linhas.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define LINESIZE 1024 | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | FILE *arq ; | ||
+ | int i ; | ||
+ | char line[LINESIZE+1] ; | ||
+ | |||
+ | // abre o arquivo em leitura | ||
+ | arq = fopen (" | ||
+ | if ( ! arq ) | ||
+ | { | ||
+ | | ||
+ | exit (1) ; | ||
+ | } | ||
+ | |||
+ | // lê as 10 primeiras linhas do arquivo | ||
+ | for (i = 0; i < 10; i++) | ||
+ | { | ||
+ | fgets (line, LINESIZE, arq) ; | ||
+ | printf ("%d: %s", i, line) ; | ||
+ | } | ||
+ | |||
+ | // fecha o arquivo | ||
+ | fclose (arq) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Leitura formatada ==== | ||
+ | |||
+ | Lê dados do //stream// '' | ||
+ | |||
+ | <code c> | ||
+ | int scanf (const char* format, ...) | ||
+ | </ | ||
+ | |||
+ | Similar a '' | ||
+ | |||
+ | <code c> | ||
+ | int fscanf (FILE* stream, const char* format, ...) | ||
+ | </ | ||
+ | |||
+ | Similar a '' | ||
+ | |||
+ | <code c> | ||
+ | int sscanf (const char* s, const char* format, ...) | ||
+ | </ | ||
+ | |||
+ | O exemplo a seguir lê 10 valores reais de um arquivo: | ||
+ | |||
+ | <code c le-valores.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | FILE *arq ; | ||
+ | int i ; | ||
+ | float value ; | ||
+ | |||
+ | // abre o arquivo em leitura | ||
+ | arq = fopen (" | ||
+ | if ( ! arq ) | ||
+ | { | ||
+ | | ||
+ | exit (1) ; | ||
+ | } | ||
+ | |||
+ | // lê os 10 primeiros valores do arquivo | ||
+ | for (i = 0; i < 10; i++) | ||
+ | { | ||
+ | fscanf (arq, " | ||
+ | printf ("%d: %f\n", i, value) ; | ||
+ | } | ||
+ | |||
+ | // fecha o arquivo | ||
+ | fclose (arq) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Experimente executá-lo com os dados de entrada abaixo. Pode explicar o que acontece? | ||
+ | |||
+ | <code txt numeros.txt> | ||
+ | 10 21 4 | ||
+ | 23.7 55 -0.7 | ||
+ | 6 5723.8, 455 | ||
+ | 1, 2, 3, 4 | ||
+ | </ | ||
+ | |||
+ | <note tip> | ||
+ | A função '' | ||
+ | |||
+ | Um bloco de leitura mais robusto, imune a esse problema, seria: | ||
+ | |||
+ | <code c> | ||
+ | // lê os 10 primeiros valores do arquivo | ||
+ | i = 0 ; | ||
+ | while (i < 10) | ||
+ | { | ||
+ | ret = fscanf (arq, " | ||
+ | |||
+ | // fim de arquivo ou erro? | ||
+ | if (ret == EOF) | ||
+ | break ; | ||
+ | |||
+ | // houve leitura? | ||
+ | if (ret > 0) | ||
+ | { | ||
+ | printf ("%d: %f\n", i, value) ; | ||
+ | i++ ; | ||
+ | } | ||
+ | // não houve, tira um caractere e tenta novamente | ||
+ | else | ||
+ | fgetc (arq) ; | ||
+ | } | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | ==== Fim de arquivo ==== | ||
+ | |||
+ | Muitas vezes deseja-se ler os dados de um arquivo até o fim, mas não se conhece seu tamanho a priori. Para isso existem funções e macros que indicam se o final de um arquivo foi atingido. | ||
+ | |||
+ | A função recomendada para testar o final de um arquivo é '' | ||
+ | |||
+ | <code c numera-todas.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define LINESIZE 1024 | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | FILE *arq ; | ||
+ | int i ; | ||
+ | char line[LINESIZE+1] ; | ||
+ | |||
+ | // abre o arquivo em leitura | ||
+ | arq = fopen (" | ||
+ | if ( ! arq ) | ||
+ | { | ||
+ | | ||
+ | exit (1) ; | ||
+ | } | ||
+ | |||
+ | // lê TODAS as linhas do arquivo | ||
+ | i = 1 ; | ||
+ | fgets (line, LINESIZE, arq) ; // tenta ler uma linha | ||
+ | while (! feof (arq)) | ||
+ | { | ||
+ | printf ("%d: %s", i, line) ; | ||
+ | fgets (line, LINESIZE, arq) ; // tenta ler a próxima linha | ||
+ | i++ ; | ||
+ | } | ||
+ | |||
+ | // fecha o arquivo | ||
+ | fclose (arq) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | <note important> | ||
+ | Observe que a função '' | ||
+ | </ | ||
+ | |||
+ | A macro '' | ||
+ | |||
+ | <code c le-eof.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define LINESIZE 1024 | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | FILE *arq ; | ||
+ | char c ; | ||
+ | |||
+ | // abre o arquivo em leitura | ||
+ | arq = fopen (" | ||
+ | if ( ! arq ) | ||
+ | { | ||
+ | | ||
+ | exit (1) ; | ||
+ | } | ||
+ | |||
+ | // lê os caracteres até o fim do arquivo | ||
+ | c = fgetc (arq) ; // tenta ler um caractere | ||
+ | while (c != EOF) // não é o fim do arquivo | ||
+ | { | ||
+ | printf ("%c ", c) ; // tenta ler o próximo | ||
+ | c = fgetc (arq) ; | ||
+ | } | ||
+ | |||
+ | // fecha o arquivo | ||
+ | fclose (arq) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Por fim, esta função retorna um valor não nulo se ocorreu um erro no último acesso ao //stream//: | ||
+ | |||
+ | <code c> | ||
+ | int ferror (FILE* stream) | ||
+ | </ | ||
+ | |||
+ | Além de ajustar o indicador de erro do //stream//, as funções de acesso a //streams// também ajustam a variável '' | ||
+ | |||
+ | ===== Exercícios ===== | ||
+ | |||
+ | - Escreva um programa em C para informar o número de caracteres presentes em um arquivo de texto. | ||
+ | - Escreva um programa em C que leia um arquivo de texto com números reais (um número por linha) e informe a média dos valores lidos. | ||
+ | - Escreva um programa em C para ler um arquivo '' | ||
+ | - O arquivo {{ mapa.txt}} contém o mapa de um nível do jogo [[https:// | ||
+ | - Escreva um programa '' | ||
+ | - o comando '' | ||
+ | |||
+ | Mais exercícios no capítulo 11 da {{apostila_c_-_nce.pdf|apostila do NCE/UFRJ}}. | ||