Diferenças

Aqui você vê as diferenças entre duas revisões dessa página.

Link para esta página de comparações

Ambos lados da revisão anterior Revisão anterior
c:arquivos_binarios [2023/08/01 20:10] – edição externa 127.0.0.1c:arquivos_binarios [2023/08/15 14:52] (atual) maziero
Linha 1: Linha 1:
 +====== Arquivos binários ======
 +
 +{{ progc_arquivos_binarios.mkv |Video desta aula}}
 +
 +Todos os arquivos contêm sequências de bytes, mas costuma-se dizer que um arquivo é "binário" quando seu conteúdo não é uma informação textual (ou seja, não representa um texto usando codificações como ASCII, UTF-8 ou outras). Arquivos binários são usados para armazenar informações mais complexas como imagens, música, código executável, etc.
 +
 +Em C, um arquivo binário é visto como uma sequência de blocos de mesmo tamanho. O tamanho dos blocos depende do tipo de informação armazenada no arquivo. Por exemplo, um arquivo de números reais ''double'' terá blocos de 8 bytes, enquanto um arquivo de caracteres (''char'') terá blocos de 1 byte, como mostra a figura:
 + 
 +{{ binary-files.png |Blocos em arquivos binários}}
 +
 +<note important>
 +Lembre-se que o SO só armazena a sequência de bytes, sem considerar nem registrar o tamanho dos blocos. **Cabe à aplicação** definir o tamanho de bloco que deseja usar em cada arquivo.
 +</note>
 +
 +==== Leitura/escrita de blocos ====
 +
 +A linguagem C oferece funções para ler e escrever blocos de bytes em arquivos, que efetuam a cópia desses bytes da memória para o arquivo ou vice-versa.
 +
 +A funções a seguir permitem ler/escrever blocos de bytes em arquivos binários. Todas essas funções estão definidas no arquivo ''stdio.h''.
 +
 +Lê até ''count'' blocos de tamanho ''size'' bytes cada um e os deposita no vetor ''data'', a partir do //stream// indicado. Retorna o número de blocos lidos:
 +
 +<code c>
 +size_t fread (void* data, size_t size, size_t count, FILE* stream)
 +</code>
 +
 +Escreve até ''count'' blocos de tamanho ''size'' bytes do vetor ''data'' no //stream// indicado. Retorna o número de blocos escritos:
 +
 +<code c>
 +size_t fwrite (const void* data, size_t size, size_t count, FILE* stream)
 +</code>
 +
 +<note tip>
 +Essas funções também podem ser usadas para ler/escrever em arquivos-texto, pois textos são sequências de blocos de 1 byte.
 +</note>
 +
 +==== Exemplo de uso ====
 +
 +Este exemplo manipula um arquivo binário ''numeros.dat'' contendo números reais. São implementadas (em arquivos separados) as operações de escrita de números no arquivo, listagem e ordenação do conteúdo:
 +
 +<code c escreve.c>
 +// Escreve N valores reais aleatórios em um arquivo, em formato binário
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <time.h>
 +
 +#define ARQUIVO "numeros.dat"
 +#define NUMVAL 10
 +
 +int main (int argc, char *argv[])
 +{
 +  FILE* arq ;
 +  int i, ret ;
 +  float value[NUMVAL] ;
 +
 +  // abre o arquivo em modo "append"
 +  arq = fopen (ARQUIVO, "a") ;
 +  if (!arq)
 +  {
 +    perror ("Erro ao abrir arquivo") ;
 +    exit (1) ;
 +  }
 +
 +  // inicia gerador de números aleatórios
 +  srand (clock()) ;
 +
 +  // gera NUMVAL valores aleatórios reais
 +  for (i = 0; i < NUMVAL; i++)
 +    value[i] = random() / 100000.0 ;
 +
 +  // escreve os valores gerados no final do arquivo
 +  ret = fwrite (value, sizeof (float), NUMVAL, arq) ;
 +  if (ret)
 +    printf ("Gravou %d valores com sucesso!\n", ret) ;
 +  else
 +    printf ("Erro ao gravar...\n") ;
 +
 +  // fecha o arquivo
 +  fclose (arq) ;
 +  return (0) ;
 +}
 +</code>
 +
 +<code c lista.c>
 +// Lista o conteúdo de um arquivo que contém números reais em formato binário 
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#define ARQUIVO "numeros.dat"
 +
 +int main (int argc, char *argv[])
 +{
 +  FILE* arq ;
 +  float value ;
 +
 +  // abre o arquivo em modo leitura
 +  arq = fopen (ARQUIVO, "r") ;
 +  if (!arq)
 +  {
 +    perror ("Erro ao abrir arquivo") ;
 +    exit (1) ;
 +  }
 +
 +  // lê e imprime os valores contidos no arquivo
 +  fread (&value, sizeof (float), 1, arq) ;
 +  while (! feof (arq))
 +  {
 +    printf ("%f\n", value) ;
 +    fread (&value, sizeof (float), 1, arq) ;
 +  }
 +
 +  // fecha o arquivo
 +  fclose (arq) ;
 +  return (0) ;
 +}
 +</code>
 +
 +<code c ordena.c>
 +// Lê os números reais de um arquivo binário, os ordena e os escreve de volta no arquivo
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +#define ARQUIVO "numeros.dat"
 +#define MAXVAL 100000
 +
 +float value[MAXVAL] ;
 +int num_values ;
 +
 +int main (int argc, char *argv[])
 +{
 +  FILE* arq ;
 +  int i, j, menor ;
 +  float aux ;
 +
 +  // abre o arquivo em leitura/escrita, preservando o conteúdo
 +  arq = fopen (ARQUIVO, "r+") ;
 +  if (!arq)
 +  {
 +    perror ("Erro ao abrir arquivo") ;
 +    exit (1) ;
 +  }
 +
 +  // lê números do arquivo no vetor
 +  num_values = fread (value, sizeof (float), MAXVAL, arq) ;
 +  printf ("Encontrei %d números no arquivo\n", num_values) ;
 +
 +  // ordena os números (por seleção)
 +  for (i = 0; i < num_values-1; i++)
 +  {
 +    // encontra o menor elemento no restante do vetor
 +    menor = i ;
 +    for (j = i+1; j < num_values; j++)
 +      if (value[j] < value[menor])
 +        menor = j ;
 +
 +    // se existe menor != i, os troca entre si
 +    if (menor != i)
 +    {
 +      aux = value[i] ;
 +      value[i] = value[menor] ;
 +      value[menor] = aux ;
 +    }
 +  }
 +
 +  // retorna o ponteiro ao início do arquivo
 +  rewind (arq) ;
 +
 +  // escreve números do vetor no arquivo
 +  fwrite (value, sizeof (float), num_values, arq) ;
 +
 +  // fecha o arquivo
 +  fclose (arq) ;
 +  return (0) ;
 +}
 +</code>
 +
 +<note tip>
 +No arquivo ''ordena.c'', o conteúdo inteiro do arquivo é lido com **apenas uma** chamada ''fread'' e escrito com apenas uma chamada ''fwrite''. Isso é perfeitamente possível, desde que a estrutura usada para receber os dados na memória coincida byte a byte com a forma como eles estão dispostos no arquivo.
 +</note>
 +
 +===== Posicionamento no arquivo =====
 +
 +Para cada arquivo aberto em uma aplicação, o sistema operacional mantém um contador interno indicando a posição da próxima operação de leitura ou escrita. Esse contador é conhecido como **ponteiro de posição** (embora não seja realmente um ponteiro).
 +
 +Por default, as operações em um arquivo em C ocorrem em posições sucessivas dentro do arquivo: cada leitura (ou escrita) corre **após** a leitura (ou escrita) precedente, até atingir o final do arquivo. Essa forma de acesso ao arquivo é chamada de **acesso sequencial**.
 +
 +Por vezes uma aplicação precisa ler ou escrever em posições específicas de um arquivo, ou precisa voltar a ler uma posição do arquivo que já percorreu anteriormente. Isso ocorre frequentemente em aplicações que manipulam arquivos muito grandes, como vídeos ou bases de dados. Para isso é necessária uma forma de **acesso direto** a posições específicas do arquivo.
 +
 +Em C, o acesso direto a posições específicas de um arquivo é feita através de funções de **posicionamento de ponteiro**, que permitem alterar o valor do ponteiro de posição do arquivo, antes da operação de leitura/escrita desejada.
 +
 +<note important>
 +Todas as funções de manipulação do ponteiro de arquivo consideram as **posições em bytes** a partir do início do arquivo, nunca em número de blocos.
 +</note>
 +
 +As funções mais usuais para acessar o ponteiro de posição de um arquivo em C são indicadas a seguir.
 +
 +Ajusta posição do ponteiro no //stream// indicado:
 +
 +<code c>
 +int fseek (FILE* stream, long int offset, int whence)
 +</code>
 +
 +O ajuste é definido por ''offset'', enquanto o valor de ''whence'' indica se o ajuste é relativo ao início do arquivo (''SEEK_SET''), à posição corrente (''SEEK_CUR'') ou ao final do arquivo (''SEEK_END''). Ver também ''fseeko'' e ''fseeko64''. Exemplos:
 +
 +<code c>
 +                              // posiciona o ponteiro de "arq":
 +fseek (arq, 1000, SEEK_SET) ; // 1000 bytes após o início
 +fseek (arq, -300, SEEK_END) ; // 300 bytes antes do fim 
 +fseek (arq, -500, SEEK_CUR) ; // 500 bytes antes da posição atual
 +</code>
 +
 +Reposiciona o ponteiro no início (posição 0) do //stream// indicado: 
 +
 +<code c>
 +void rewind (FILE* stream)
 +</code>
 +
 +Informa a posição corrente de leitura/escrita em ''stream'' (ver também ''ftello'' e ''ftello64'' no manual).
 +
 +<code c>
 +long int ftell (FILE* stream)
 +</code>
 +
 +===== Outras funções =====
 +
 +Para truncar ("encurtar") um arquivo, deixando somente os primeiros ''length'' bytes:
 +
 +<code c>
 +#include <unistd.h>
 +#include <sys/types.h>
 +
 +// usando o nome do arquivo, sem abri-lo
 +int truncate (const char *path, off_t length);
 +
 +// usando um descritor UNIX (fd)
 +int ftruncate (int fd, off_t length);
 +</code>
 +
 +Para obter as propriedades (metadados) de um arquivo:
 +
 +<code c>
 +#include <sys/types.h>
 +#include <sys/stat.h>
 +#include <unistd.h>
 +
 +// usando o nome do arquivo, sem abri-lo
 +int stat  (const char *pathname, struct stat *statbuf);
 +
 +// usando um descritor UNIX (fd)
 +int fstat (int fd, struct stat *statbuf);
 +</code>
 +
 +As informações sobre o arquivo serão depositadas pelo núcleo na estrutura ''statbuf'', cujo conteúdo está descrito abaixo. Os campos da estrutura são detalhados na página de manual da função ''fstat()''.
 +
 +<code c>
 +struct stat
 +{
 +  dev_t     st_dev;         /* ID of device containing file */
 +  ino_t     st_ino;         /* Inode number */
 +  mode_t    st_mode;        /* File type and mode */
 +  nlink_t   st_nlink;       /* Number of hard links */
 +  uid_t     st_uid;         /* User ID of owner */
 +  gid_t     st_gid;         /* Group ID of owner */
 +  dev_t     st_rdev;        /* Device ID (if special file) */
 +  off_t     st_size;        /* Total size, in bytes */
 +  blksize_t st_blksize;     /* Block size for filesystem I/O */
 +  blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */
 +  struct timespec st_atim;  /* Time of last access */
 +  struct timespec st_mtim;  /* Time of last modification */
 +  struct timespec st_ctim;  /* Time of last status change */
 +  #define st_atime st_atim.tv_sec  /* For backward compatibility */
 +  #define st_mtime st_mtim.tv_sec
 +  #define st_ctime st_ctim.tv_sec
 +};
 +</code>
 +
 +===== Exercícios =====
 +
 +  - Escreva três programas C separados para:
 +    - escrever um arquivo com n (n é um número aleatório > 100) inteiros ''long'' aleatórios, armazenados em modo binário;
 +    - ler o arquivo de inteiros em um vetor, ordenar o vetor e reescrever o arquivo;
 +    - escrever na tela os primeiros 10 números e os últimos 10 números contidos no arquivo.
 +    - Utilize stat ou fstat para recuperar o tamanho do arquivo
 +
 +Mais exercícios no capítulo 11 da {{apostila_c_-_nce.pdf|apostila do NCE/UFRJ}}.