binary file icon Arquivos Binários

Last updated: June 10th, 2018

Introdução

  • Estamos estudando arquivos. Lembre-se que arquivos nada mais são do que uma abstração do sistema operacional para dados não voláteis. Para nós, um arquivo é simplesmente uma sequência de bytes.
  • Já vimos arquivos de texto, onde cada byte representa um caractere, e agora vamos conhecer os arquivos do tipo binário.
  • Binário é o formato mais genérico e de mais baixo nível no computador. De fato, qualquer arquivo no computador pode ser visto como um arquivo binário.
  • Nesses arquivos, realizamos operações de leitura e escrita em blocos de bytes. Quem decide o que cada byte significa é o criador do arquivo. Isso fornece grande flexibilidade, mas significa que esses arquivos são difíceis de serem lidos por humanos, e precisam ser tratados por programas que saibam o que cada byte significa.
  • A maior vantagem dos arquivos binários é poder salvar as informações exatamente como estão na memória. Podemos inclusive guardar estruturas inteiras, como structs e vetores.
  • Visualizar um arquivo binário é complicado. Existem programas para editar arquivos binários que permitem sua visualização. Esses arquivos mostram o valor de cada byte em hexadecimal, mas a interpretação deve ser feita manualmente. Vejamos um exemplo.
    5353 2065 6d20 4150 4320 6f2f

    Esta é uma saída típica da representação hex de um arquivo binário. Como cada dígito hexadecimal equivale a 4 bits, e cada byte tem 8 bits, então cada 2 dígitos hexadecimais são 1 byte. Mas o que esses bytes significam?

    O significado depende da interpretação.

    • Vetor de inteiros: 1397956709, 1830830416, 1126199087
    • Vetor de floats: 9.06782e+11, 3.09978e+27, 160.434
    • String: SS em APC o/

    Opções para visualização de arquivos binários são WinHex (Windows) e os comandos hexdump ou xxd (Linux)

Abrindo Arquivos Binários

Abrir arquivo binários é muito parecido com abrir arquivos texto, usamos a mesma função fopen(), e um ponteiro para arquivo também. A diferença é no modo de abertura. Para arquivos binários devemos informar ao SO que iremos abrir o arquivo no modo binário, adicionando um "b" ao final do modo. Assim, os modos de abertura binários são:

Code Nome Descrição
"rb" Read Abre um arquivo BINÁRIO para leitura, já deve existir o arquivo.
"wb" Write Abre (ou cria) um arquivo BINÁRIO para escrita. Se o arquivo já existir seu conteúdo anterior é descartado.
"ab" Append Abre (ou cria) um arquivo BINÁRIO para escrita. Ponteiro começa no final do arquivo.
"r+b" Read Plus Abre um arquivo BINÁRIO para leitura ou escrita. O arquivo deve existir e poder ser modificado.
"w+b" Write Plus Abre (ou cria) um arquivo BINÁRIO para leitura e escrita. Se o arquivo já existir seu conteúdo anterior é descartado.
"a+b" Append Plus Abre (ou cria) um arquivo BINÁRIO para leitura e escrita. Ponteiro começa no final do arquivo.

Um comando típico para abrir um arquivo binário em modo de leitura seria:

FILE* fd; // file descriptor
char file[] = "file.bin"; // file name
fd = fopen(file, "wb"); // abrindo arquivo para escrita

Lembre-se de sempre fechar os arquivos depois de usá-los. Isso com certeza vale para arquivos binários também. A função usada é fclose(fd)

Leitura e Escrita em Arquivos Binários

Em arquivos binários realizamos operações de leitura e escrita em blocos de bytes através das funções abaixo.

int fread(void* ptr, int size, int count, FILE* stream ); // leitura
int fwrite(const void* ptr, int size, int count, FILE* stream ); // escrita
Os parâmetros dessas funções são:
  • ptr é o buffer de transferência, isto é, a região de memória na de/para onde os dados serão lidos/escritos.
  • size é o tamanho, em bytes, de um bloco individual.
  • count é a quantidade de blocos.
  • stream é o ponteiro para o arquivo manipulado.

Note que size * count representa o total de bytes a serem lidos/escritos. A função sizeof() é muito útil para descobrir o tamanho em bytes de uma variável.

Ambas as funções retornam o total de blocos trnansferidos com sucesso. Podemos usar esse valor para verificar se houve algum problema na leitura/escrita.

Exemplo de escrita

  • Utilizando o mesmo exemplo da aula passada, com a struct Estudante e o arquivo notas.txt

typedef struct{
    char nome[100];
    double nota;
}Estudante;

int main(){

    int i;
    Estudante aluno;
    FILE *arquivo;
    arquivo = fopen("notas.bin","wb");

    if (arquivo == NULL){
       printf("Error! opening file");
       return 1;
    }

    for(i = 0; i < 3; i++){
        scanf("%s", aluno.nome);
        scanf("%lf", &aluno.nota);
        fwrite(&aluno, sizeof(Estudante), 1, arquivo); 
    }
    fclose(arquivo); 
  
   return 0;
}
  • Note que conseguimos salvar a struct inteira dentro do arquivo notas.bin

Exemplo de leitura

typedef struct{
    char nome[100];
    double nota;
}Estudante;

int main()
{
    int i;
    Estudante aluno;
    FILE *arquivo;

    if ((arquivo = fopen("notas.bin","rb")) == NULL){
        printf("Error! opening file");
        return 1;
    }

    for(n = 0; n < 3; ++n){
        fread(&aluno, sizeof(Estudante), 1, arquivo); 
        printf("nome : %s\n nota : %lf\n",aluno.nome, aluno.nota);
    }
    
    fclose(arquivo); 
    return 0;
}

Verificando o Estado dos arquivos

É uma boa prática verificar o estado do arquivo após cada operação com ele. Ao final de cada chamada de função que manipule o arquivo, se ocorreu qualquer erro ele fica armazenado no ponteiro do arquivo (fd). Podemos acessar essas informações com as funções abaixo.

feof(fd); // != 0 if End Of File
ferror(fd); // != 0 if Error Occured