From 76a3f778827dc7b9e1ff67be02ff904fda2c33c3 Mon Sep 17 00:00:00 2001 From: Blau Araujo Date: Wed, 7 May 2025 08:54:19 -0300 Subject: [PATCH] =?UTF-8?q?conte=C3=BAdo=20da=20aula=2016?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aulas/16-rwfiles/README.org | 245 ++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 aulas/16-rwfiles/README.org diff --git a/aulas/16-rwfiles/README.org b/aulas/16-rwfiles/README.org new file mode 100644 index 0000000..7b47605 --- /dev/null +++ b/aulas/16-rwfiles/README.org @@ -0,0 +1,245 @@ +#+title: Curso Básico da Linguagem C +#+subtitle: Aula 16: Abertura de arquivos para leitura e escrita +#+author: Blau Araujo +#+startup: show2levels +#+options: toc:3 + +* Aula 16: Abertura de arquivos para leitura e escrita + +[[https://youtu.be/B42KIZfivsg][Vídeo desta aula]] + +** Modos de abertura para leitura e escrita + +As strings de modos ="r"=, ="w"= e ="a"=, da função =fopen=, quando sucedidas do +caractere =+=, indicam a abertura do arquivo também para suas respectivas +operações contrarias: + +| Modo | Leitura | Escrita | Truncar | Offset | Criar | +|------+---------+---------+---------+--------+-------| +| ="r"= | Sim | Não | Não | Início | Não | +| ="r+"= | Sim | Sim | Não | Início | Não | +| ="w"= | Não | Sim | Sim | Início | Sim | +| ="w+"= | Sim | Sim | Sim | Início | Sim | +| ="a"= | Não | Sim | Não | Fim | Sim | +| ="a+"= | Sim | Sim | Não | Fim | Sim | + +De acordo com a tabela acima, nós precisamos observar alguns pontos +importantes: + +- Se a string de modo iniciar com =w=, o arquivo sempre será truncado + na sua abertura. +- Se a string de modo iniciar com =a=, o ponteiro interno sempre será + posicionado no fim do arquivo na sua abertura. +- Se a string de modo iniciar com =r=, o ponteiro interno sempre será + posicionado no início do arquivo na sua abertura. +- Se não existir, o arquivo só será criado se a string de modo + iniciar com =w= ou =a=. + +** Manipulação do ponteiro interno + +O ponteiro interno do arquivo representa o número do byte que será lido +ou escrito no próximo acesso: ou seja, seu /offset/ (ou /ponto de partida/). +Isso é particularmente importante quando abrimos arquivos para ambas as +operações com as strings de modo ="r+"= ou ="a+"=, visto que o modo ="w+"= +truncará o conteúdo do arquivo. + +*** A função =fseek= + +As funções da família =fseek= (=stdio.h=) são utilizadas para posicionar +ou obter informações sobre o ponteiro interno do arquivo: + +| Função | Descrição | Retorno | +|---------+-----------------------------------------------------------------------------------------+--------------------------------| +| =fseek= | Define a posição do ponteiro interno. | Sucesso: =0= (=int=) | +| =ftell= | Obtém a posição corrente do ponteiro interno. | Sucesso: número do byte (=long=) | +| =rewind= | Posiciona o ponteiro interno no byte =0=. | =void= | +| =fsetpos= | Interface para =fseek= referenciando a definição do /offset/ a partir do início do arquivo. | Sucesso: =0= (=int=) | +| =fgetpos= | Interface para =ftell= escrevendo o /offset/ obtido em um dado endereço. | Sucesso: =0= (=int=) | + +*Notas:* + +- Com a função =fseek=, o novo /offset/ é definido com uma expressão do tipo =long=. +- Com a função =fsetpos=, o novo /offset/ é definido em um ponteiro do tipo =fpos_t=. +- Com a função =fgetpos=, o /offset/ corrente é escrito em um ponteiro do tipo =fpos_t=. + +#+begin_quote +A função =fseek= trabalha com /streams/, mas é possível manipular o ponteiro interno +a partir de descritores de arquivos com a chamada de sistema =lseek=. +#+end_quote + +*** Pontos de origem de =fseek= + +A função =fseek= recebe 3 argumentos: + +- O /stream/ associado ao arquivo; +- A expressão do valor do /offset/ (tipo =long=); +- Um inteiro determinando o ponto de origem para a definição do novo /offset/. + +Este é o seu protótipo na =glibc= (=stdio.h=): + +#+begin_src c +int fseek(FILE *stream, long offset, int whence); +#+end_src + +#+begin_quote +O termo /whence/ significa /"a partir de onde"/ e representa o ponto de origem +do deslocamento do ponteiro interno do arquivo. +#+end_quote + +O terceiro argumento (=whence=) pode receber qualquer valor inteiro, mas +a =glibc= oferece três macros para os pontos de origem mais comuns: + +- =SEEK_SET=: o início do arquivo; +- =SEEK_END=: o fim do arquivo; +- =SEEK_CUR=: o /offset/ corrente. + +*** Abertura de arquivos com "r+" + +Como o /offset/ inicial será o byte =0=, se a primeira operação for uma +escrita, os primeiros bytes do arquivo serão sobrescritos. Caso esta +não seja a intenção, será preciso deslocar o ponteiro interno para o +fim do arquivo: + +#+begin_src c +// Abertura do arquivo para leitura e escrita... +char *file = "arquivo.txt"; +FILE *stream = fopen(file, "r+"); + +// Posicionar offset no fim do arquivo... +fseek(stream, 0, SEEK_END); + +// Rotinas de escrita... +#+end_src + +Aqui, nós deslocamos o ponteiro interno em zero bytes a partir do fim do +arquivo (origem em =SEEK_END=). + +*** Abertura de arquivos com "a+" + +O /offset/ inicial será o fim do arquivo e, se quisermos começar a leitura +a partir de seu início, será preciso deslocar o ponteiro interno para lá: + +#+begin_src c +// Abertura do arquivo para leitura e escrita... +char *file = "arquivo.txt"; +FILE *stream = fopen(file, "r+"); + +// Posicionar offset no início do arquivo... +fseek(stream, 0, SEEK_SET); + +// Rotinas de leitura... +#+end_src + +Desta vez, nós deslocamos o ponteiro interno em zero bytes a partir do +início do arquivo (origem em =SEEK_SET=). + +** Procedimentos úteis + +Para encerrar esta aula (e o nosso curso), aqui estão alguns exemplos de +procedimentos gerais úteis envolvendo a manipulação de arquivos. + +*** Quantidade de linhas + + +#+begin_src c +#include +#include +#include + +int main(void) { + // Abertura do arquivo para leitura... + char *file = "meuarquivo.txt"; + FILE *stream = fopen(file, "r"); + if (!stream) { + fprintf(stderr, "Erro ao abrir o arquivo!\n"); + return EXIT_FAILURE; + } + + // Contagem de linhas... + char buf[BUFSIZ]; // Buffer para ler blocos do arquivo (8192 bytes). + int lines = 0; // Total de linhas encontradas. + + // Lê uma linha até encontrar \n, até BUFSIZ-1 ou até EOF... + while (fgets(buf, BUFSIZ, stream) != NULL) { + // Só conta uma linha se houver \n no buffer... + if (buf[strlen(buf) - 1] == '\n' ) lines++; + } + // Se a última linha não contiver \n, ela também será contada... + if (lines > 0 && buf[strlen(buf) - 1] != '\n') lines++; + + // Fechamento do arquivo... + fclose(stream); + + // Impressão do resultado... + printf("%d linhas", lines); + + return EXIT_SUCCESS; +} +#+end_src + +*** Obter o tamanho do arquivo + +#+begin_src c +#include +#include + +int main(void) { + // Abertura do arquivo... + char *file = "meuarquivo.txt"; + FILE *stream = fopen(file, "rb"); + if (!stream) { + perror("Erro ao abrir o arquivo"); + return EXIT_FAILURE; + } + + // Posiciona o ponteiro de leitura no final do arquivo... + if (fseek(stream, 0, SEEK_END) != 0) { + perror("Erro ao deslocar o offset para o final"); + fclose(stream); + return EXIT_FAILURE; + } + + // Obtém o offset corrente (tamanho do arquivo em bytes)... + long fsize = ftell(stream); + if (fsize == -1L) { + perror("Erro ao obter o offset corrente"); + fclose(stream); + return EXIT_FAILURE; + } + + // Exibe o tamanho do arquivo... + printf("Tamanho do arquivo: %ld bytes\n", fsize); + + // Fechamento do arquivo... + fclose(stream); + return EXIT_SUCCESS; +} +#+end_src + +*** Testar se um arquivo está vazio (0 bytes) + +#+begin_src c +#include +#include + +int main(void) { + // Abertura do arquivo... + char *file = "meuarquivo.txt"; + FILE *stream = fopen(file, "r"); + if (!stream) { + perror("Erro na abertura do arquivo"); + return EXIT_FAILURE; + } + + // is_empty recebe 1 se o arquivo estiver vazio... + int is_empty = (fseek(stream, 0, SEEK_END) == 0 && ftell(stream) == 0); + + // Fechamento do arquivo... + fclose(stream); + + // O programa terminará com sucesso (0) se o arquivo estiver vazio... + return !is_empty; +} +#+end_src + +