mirror of
https://gitlab.com/blau_araujo/cblc.git
synced 2025-05-10 02:26:36 -03:00
200 lines
6.9 KiB
Org Mode
200 lines
6.9 KiB
Org Mode
#+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
|
|
|
|
|