Arquivos binários

Arquivos binários são bem parecidos com matrizes de estruturas, exceto pelas estruturas, que residem no arquivo de disco, em vez de uma matriz na memória. Uma vez que as estruturas em um arquivo binário estão em disco, você pode criar coleções muito grandes desses arquivos (limitadas apenas pelo espaço em disco disponível). Elas também são permanentes e estão sempre disponíveis. A única desvantagem é a lentidão decorrente do tempo de acesso ao disco.

Os arquivos binários têm duas características que os distinguem de arquivos de texto:

  • Você pode pular instantaneamente para qualquer estrutura no arquivo que ofereça acesso aleatório como em uma matriz;
  • Você pode mudar o conteúdo de uma estrutura em qualquer lugar e hora no arquivo.
Arquivos binários geralmente têm tempos de leitura e escrita mais rápidos que os arquivos de texto, já que uma imagem binária do registro é armazenada diretamente da memória para o disco (ou vice-versa). Em um arquivo de texto, tudo precisa ser convertido de trás para frente em texto e isto consome tempo.

A linguagem C suporta o conceito de arquivo de estruturas de forma básica. Ao abrir o arquivo, você pode ler uma estrutura, escrever uma estrutura ou pesquisar qualquer estrutura no arquivo. Este conceito de arquivo suporta o conceito de um ponteiro de arquivo. Quando ele é aberto, o ponteiro aponta para o registro 0 (o primeiro registro no arquivo). Qualquer operação de leitura lê a estrutura apontada e move o ponteiro um nível abaixo na estrutura. Qualquer operação de escrita grava na estrutura apontada e move o ponteiro um nível abaixo na estrutura. Seek move o ponteiro para o registro solicitado.

Lembre-se de que a linguagem C considera tudo no arquivo de disco como blocos de bytes lidos do disco na memória ou lidos da memória para o disco. A linguagem C usa um ponteiro de arquivo, mas pode apontar para qualquer byte no arquivo.

O programa abaixo ilustra estes conceitos:

  #include <stdio.h>

  /* descrição de registro aleatório - pode ser qualquer uma */
  struct rec
  {
      int x,y,z;
  }; 

  /* grava e depois lê 10 registros arbitrários
     do arquivo "junk". */
  int main()
  {
      int i,j;
      FILE *f;
      struct rec r;

      /* cria o arquivo de 10 registros */
      f=fopen("junk","w");
      if (!f)
          return 1;
      for (i=1;i<=10; i++)
      {
         r.x=i;
         fwrite(&r,sizeof(struct rec),1,f);
      }
      fclose(f);

      /* lê os 10 registros */
      f=fopen("junk","w");
      if (!f)  
          return 1;
      for (i=1;i<=10; i++)
      { 
        fread(&r,sizeof(struct rec),1,f);  
        printf("%d\n",r.x);  
      } 
      fclose(f);
      printf("\n");  
       
      /* usa fseek para ler os 10 registros 
        em ordem inversa */
      f=fopen("junk","w");
      if (!f) 
          return 1;
      for (i=9; i>=0; i--)
      {
          fseek(f,sizeof(struct rec)*i,SEEK_SET);
          fread(&r,sizeof(struct rec),1,f); 
          printf("%d\n",r.x);  
      }
      fclose(f);
      printf("\n");  
      
      /* usa fseek para ler todo os outros registros */
      f=fopen("junk","r");
      if (!f)
          return 1;
      fseek(f,0,SEEK_SET);
      for (i=0;i<5; i++) 
      {
          fread(&r,sizeof(struct rec),1,f);
          printf("%d\n",r.x);  
          fseek(f,sizeof(struct rec),SEEK_CUR);
      }
      fclose(f);
      printf("\n");  

      /* usa fseek para ler o 4º registro,
         altere-o e escreva-o de volta */
      f=fopen("junk","r+");
      if (!f)
          return 1;
      fseek(f,sizeof(struct rec)*3,SEEK_SET);
      fread(&r,sizeof(struct rec),1,f);
      r.x=100; 
      fseek(f,sizeof(struct rec)*3,SEEK_SET);
      fwrite(&r,sizeof(struct rec),1,f); 
      fclose(f); 
      printf("\n");  
      
      /* lê os 10 registros para garantir
         que o 4º arquivo foi alterado */
      f=fopen("junk","r");
      if (!f)
          return 1;
      for (i=1;i<=10; i++)
      {
         fread(&r,sizeof(struct rec),1,f); 
         printf("%d\n",r.x);  
      }
      fclose(f);
      return 0; 
  } 

Neste programa, uma descrição de estrutura rec foi usada, mas você pode usar a descrição de estrutura que quiser. Você também pode ver que fopen e fclose trabalham exatamente da mesma forma que nos arquivos de texto.

As novas funções aqui são fread, fwrite e fseek. A função fread captura quatro parâmetros:

  • um endereço de memória;
  • o número de bytes a ser lido por bloco;
  • o número de blocos a ser lido;
  • a variável de arquivo.
Assim, a linha fread(&r,sizeof(struct rec),1,f); diz para ler 12 bytes (o tamanho de rec) do arquivo f (a partir do local atual do ponteiro de arquivo) no endereço de memória &r. Um bloco de 12 bytes é solicitado. Seria fácil ler 100 blocos por disco em uma matriz na memória alterando 1 para 100.

A função fwrite opera da mesma forma, mas move o bloco de bytes da memória para o arquivo. A função fseek move o ponteiro de arquivo para um byte no arquivo. Em geral, você muda o ponteiro em incrementos de sizeof(struct rec) para manter o ponteiro nos limites do registro. Você pode usar três opções ao pesquisar:

  • SEEK_SET
  • SEEK_CUR
  • SEEK_END
SEEK_SET move o ponteiro x bytes para baixo, a partir do início do arquivo (desde o byte 0 no arquivo). SEEK_CUR move o ponteiro x bytes para baixo da posição atual do ponteiro. SEEK_END move o ponteiro do final do arquivo (com esta opção você precisa usar offsets negativos).

Várias opções diferentes aparecem no código acima. Observe em particular a seção na qual o arquivo é aberto com o modo r+. Isto abre o arquivo para leitura e gravação, permitindo que os registros sejam alterados. O código busca um registro, lê e altera um campo. Depois busca novamente, pois a leitura deslocou o ponteiro, e grava de volta a alteração.

Para mais informações sobre a linguagem C e tópicos relacionados, verifique os links na próxima página.