diff --git a/aulas/14-rfiles/README.org b/aulas/14-rfiles/README.org new file mode 100644 index 0000000..81fdbc6 --- /dev/null +++ b/aulas/14-rfiles/README.org @@ -0,0 +1,200 @@ +#+title: Curso Básico da Linguagem C +#+subtitle: Aula 14: Abertura de arquivos para leitura +#+author: Blau Araujo +#+startup: show2levels +#+options: toc:3 + +* Aula 14: Abertura de arquivos para leitura + +[[https://www.youtube.com/watch?v=uh3UdYyzXRM][Vídeo desta aula]] + +Em sistemas /Unix-like/, um /arquivo/ é qualquer abstração de recursos de entrada +e saída representada como /fluxos de dados/ (/streams/) acessados por meio de +operações padronizadas, independentemente de seu conteúdo ou finalidade. + +Neste sentido, existem sete tipos de arquivos: + +- Diretórios +- Arquivos regulares (ou comuns) +- Ligações simbólicas +- Dispositivos especiais caractere +- Dispositivos especiais de bloco +- Arquivos de pipe (ou FIFO) +- Sockets + +Quando queremos acessar um arquivo, seja de que tipo for, nós precisamos que +o sistema disponibilize vários recursos, como: + +- Um /descritor de arquivo/ (/file descriptor/): um número inteiro que identifica + de forma única o arquivo aberto no contexto do processo. +- Uma tabela de descritores do processo: uma estrutura que associa os descritores + de arquivos a entradas na tabela global de arquivos abertos. +- A tabela global de arquivos abertos (/file table/): que armazena informações + como a posição atual de leitura/gravação e as flags de acesso. +- A tabela de /inodes/: que contém os metadados do arquivo, como permissões, + o "dono" do arquivo, o tipo do arquivo e o ponteiro para os dados no disco. +- Os /buffers/ do sistema: usados para otimizar acesso ao conteúdo do arquivo + acumulando dados temporariamente na memória. + +Na linguagem C, tudo isso é abstraído como o tipo =FILE *=, que é um ponteiro para +a estrutura de dados (uma /struct/) disponibilizada para cada operação de acesso +ao /fluxo de dados/ (/stream/) que irá representar o arquivo. + +Resumindo: + +- No sistema, arquivos são abstrações de recursos de entrada e saída + representados como /fluxos de dados/; +- Na linguagem C, os fluxos de dados, bem como os recursos que eles representam, + são abstraídos com o tipo =FILE *= (ponteiro para =FILE=). + +#+begin_quote +O tipo =FILE *= é definido na biblioteca padrão (cabeçalho =stdio.h=). +#+end_quote + +** Diferença entre streams e descritores de arquivos + +*** Descritores de arquivos (tipo =int=) + +- São expressos como inteiros (tipo =int=). +- Visíveis em =/proc/PID/fd=. +- Identificam arquivos em chamadas de sistema (=open=, =close=, =read=, =write=, etc). +- Representam diretamente uma entrada na tabela de arquivos abertos do processo. + +*** Streams (tipo ponteiro para =FILE=) + +- São abstrações de todos os recursos abertos para operações com arquivos. +- Utilizados em funções da biblioteca padrão (=fopen=, =fclose=, =fgets=, =fprintf=, etc). +- Utilizam descritores de arquivos para interagir com o sistema. +- Envolvem acumulação (/buffering/) na memória do processo. + +** Procedimento geral de acesso a arquivos para leitura + +Geralmente, as operações com arquivos envolvem três etapas: abertura, +processamento e fechamento. + +*** Abertura + +- Disponibilização dos recursos para acesso ao arquivo. +- Definição do tipo de operação (escrita, /append/ e/ou leitura). +- Com a chamada de sistema =open=, recebemos um descritor de arquivos. +- Com a função =fopen=, recebemos um ponteiro para =FILE= (/stream/). +- Com =fopen=, um ponteiro nulo (=NULL=) será retornado no caso de erros. +- Com =open=, o retorno será =-1= no caso de erros. +- Com ambas as funções, a variável =errno= receberá a identificação do erro. + +#+begin_quote +Neste curso, nós utilizaremos apenas a função =fopen=. +#+end_quote + +Exemplo: + +#+begin_src c +char *file = "arquivo.txt"; +FILE *stream = fopen(file, "r"); +#+end_src + +Aqui, o arquivo =arquivo.txt=, se existir no diretório corrente, será aberto +para leitura (modo de abertura ="r"=). Se tudo correr bem, nós teremos o +fluxo de dados =stream= para manipular o arquivo. + +Para tratar eventuais erros, nós podemos fazer algo assim: + +#+begin_src c +char *file = "arquivo.txt"; +FILE *stream = fopen(file, "r"); +if (!stream) { + perror("Erro ao abrir o arquivo"); + return EXIT_FAILURE; + } +#+end_src + +#+begin_quote +A função =perror= imprime, na saída padrão de erros (=stderr=), a descrição +do último erro atribuído à variável =errno=. A string, passada como argumento, +será utilizada como prefixo da mensagem impressa. +#+end_quote + +*** Processamento + +É quando nós realizamos alguma operação com o arquivo, como ler seu conteúdo, +escrever algo nele, obter seu tamanho em bytes, reposicionar seu ponteiro +interno, etc. Por exemplo, nós podemos abrir o arquivo =/etc/shells= para ler +e imprimir todo seu conteúdo: + +#+begin_src c +char *file = "/etc/shells"; + +// Abertura do arquivo... +FILE *stream = fopen(file, "r"); +if (!stream) { + perror("Erro ao abrir o arquivo"); + return EXIT_FAILURE; +} + +// Processamento... +char line[BUFSIZ]; // Buffer para acumular cada linha lida +while (fgets(line, BUFSIZ, stream)) { + printf("%s", line); +} +#+end_src + +Aqui, =fgets= lerá o conteúdo do arquivo associado a =stream= (=/etc/shells=) até +encontrar uma quebra de linha ou chagar a =BUFSIZ-1= bytes. + +#+begin_quote +A macro =BUFSIZ= é definida em =stdio.h= e expande o valor =8192= (8kB). +#+end_quote + +De um modo ou de outro, a estrutura de dados associada a =stream= registrará +a quantidade de bytes que já foram lidos para que, no caso de um acesso +subsequente, a leitura continue a partir do byte seguinte -- isso é o que +chamamos de /ponteiro interno do arquivo/. + +#+begin_quote +O ponteiro interno do arquivo pode ser obtido e manipulado com as funções +=fseek=, =ftell= e =rewind=. +#+end_quote + +Consequentemente, enquanto =fgets= não retornar =NULL= (no caso de um erro ou +do fim do arquivo), o conteúdo do buffer =line= será impresso e o arquivo +será lido novamente a partir da nova posição de seu ponteiro interno. + +*** Fechamento + +- Liberação dos recursos disponibilizados na abertura. +- Tratamento adequado para os dados pendentes. +- Possibilita a verificação de erros de fechamento. +- Com =close= ou =fclose=, o retorno será =0= em caso de sucesso. +- Com =close=, o retorno será =-1= em caso de erro. +- Com =fclose=, o retorno será =EOF= em caso de erro (que também tem valor =-1=). +- Com ambas as funções, a variável =errno= receberá a identificação do erro. + +#+begin_quote +Os arquivos abertos são fechados com o fim do processo, mas é sempre +preferível fechá-los explicitamente para garantir a integridade dos +dados e evitar vulnerabilidades (como o vazamento de recursos). +#+end_quote + +Para fechar o arquivo do nosso exemplo: + +#+begin_src c +char *file = "/etc/shells"; + +// Abertura do arquivo... +FILE *stream = fopen(file, "r"); +if (!stream) { + perror("Erro ao abrir o arquivo"); + return EXIT_FAILURE; +} + +// Processamento... +char line[BUFSIZ]; // Buffer para acumular cada linha lida +while (fgets(line, BUFSIZ, stream)) { + printf("%s", line); +} + +// Fechamento do arquivo... +fclose(stream); +#+end_src + +