Arquivos binários — fread, fwrite, fseek e acesso direto a registros
Na Aula 39a usamos fprintf para gravar dados como texto legível. Funciona, mas tem limitações:
| Aspecto | Arquivo Texto | Arquivo Binário |
|---|---|---|
| Leitura humana | ✅ Abre no Bloco de Notas | ❌ Bytes brutos — ilegível |
| Gravar/ler struct completa | ❌ Campo por campo — trabalhoso | ✅ A struct inteira de uma vez |
| Nomes com espaços | ⚠️ Precisa de tratamento extra | ✅ Funciona — grava os bytes como estão |
| Acesso direto ao registro N | ❌ Precisa varrer o arquivo | ✅ Cálculo de offset imediato |
| Velocidade para dados grandes | Mais lento — conversão para string | ✅ Mais rápido — cópia direta de memória |
struct Aluno de 60 bytes no programa ocupa exatamente 60 bytes no arquivo. Isso torna a leitura e escrita triviais e eficientes.
fwritefwrite(ptr, tamanho, quantidade, arquivo) — grava quantidade blocos de tamanho bytes a partir do endereço ptr no arquivo. Retorna o número de blocos gravados com sucesso.
#include <stdio.h>
typedef struct {
int matricula;
char nome[50];
float nota;
} Aluno; // sizeof(Aluno) ≈ 58 bytes
int main() {
FILE *f = fopen("alunos.bin", "wb"); // "wb" = write binary
if (f == NULL) { printf("Erro!\n"); return 1; }
Aluno a1 = {1001, "Ana Silva", 8.5f};
Aluno a2 = {1002, "Bruno Lima", 6.0f};
Aluno a3 = {1003, "Carla Mota", 9.5f};
/* grava 1 struct por vez */
fwrite(&a1, sizeof(Aluno), 1, f);
fwrite(&a2, sizeof(Aluno), 1, f);
fwrite(&a3, sizeof(Aluno), 1, f);
fclose(f);
printf("3 alunos gravados em alunos.bin\n");
return 0;
}
O arquivo alunos.bin terá exatamente 3 × sizeof(Aluno) bytes. Não há separadores, quebras de linha nem conversão — são os bytes puros de cada struct.
Aluno turma[3] = {
{1001, "Ana Silva", 8.5f},
{1002, "Bruno Lima", 6.0f},
{1003, "Carla Mota", 9.5f}
};
/* grava os 3 de uma vez — equivalente a 3 chamadas separadas */
fwrite(turma, sizeof(Aluno), 3, f);
freadfread(ptr, tamanho, quantidade, arquivo) — lê quantidade blocos de tamanho bytes do arquivo e os armazena a partir do endereço ptr. Retorna o número de blocos lidos com sucesso.
#include <stdio.h>
typedef struct {
int matricula;
char nome[50];
float nota;
} Aluno;
int main() {
FILE *f = fopen("alunos.bin", "rb"); // "rb" = read binary
if (f == NULL) { printf("Arquivo não encontrado.\n"); return 1; }
Aluno a;
printf("%-6s | %-20s | %s\n", "Matr.", "Nome", "Nota");
printf("------------------------------------\n");
/* lê uma struct por vez até acabar o arquivo */
while (fread(&a, sizeof(Aluno), 1, f) == 1) {
printf("%-6d | %-20s | %.1f\n",
a.matricula, a.nome, a.nota);
}
fclose(f);
return 0;
}
fwrite grava, fread lê exatamente da mesma forma. Não há parsing, não há conversão, não há preocupação com espaços em nomes — os bytes saem da struct e entram de volta na struct sem nenhuma transformação.
fseek, ftell e rewindUma das grandes vantagens do arquivo binário com structs de tamanho fixo é o acesso direto: você pode ir diretamente ao registro N sem ler todos os anteriores.
fseek(arquivo, offset, origem) move o cursor do arquivo para uma posição específica.ftell(arquivo) retorna a posição atual do cursor (em bytes desde o início).rewind(arquivo) volta o cursor ao início — equivalente a fseek(f, 0, SEEK_SET).
| Origem (3º parâmetro) | Significado | Exemplo |
|---|---|---|
SEEK_SET |
Desde o início do arquivo | fseek(f, 0, SEEK_SET) — vai ao início |
SEEK_CUR |
Relativo à posição atual | fseek(f, 10, SEEK_CUR) — avança 10 bytes |
SEEK_END |
Relativo ao fim do arquivo | fseek(f, 0, SEEK_END) — vai ao final |
#include <stdio.h>
typedef struct {
int matricula;
char nome[50];
float nota;
} Aluno;
/* Lê o aluno na posição 'indice' (0-based) sem ler os outros */
int lerAlunoPorIndice(const char *caminho, int indice, Aluno *dest) {
FILE *f = fopen(caminho, "rb");
if (f == NULL) return 0;
long offset = (long)indice * sizeof(Aluno); // calcula o byte exato
fseek(f, offset, SEEK_SET); // pula direto para lá
int ok = (fread(dest, sizeof(Aluno), 1, f) == 1);
fclose(f);
return ok;
}
/* Conta quantos alunos existem no arquivo */
int contarAlunos(const char *caminho) {
FILE *f = fopen(caminho, "rb");
if (f == NULL) return 0;
fseek(f, 0, SEEK_END); // vai ao final
long bytes = ftell(f); // quantos bytes tem o arquivo
fclose(f);
return (int)(bytes / sizeof(Aluno));
}
int main() {
int total = contarAlunos("alunos.bin");
printf("Total de alunos no arquivo: %d\n\n", total);
/* Lê diretamente o último aluno */
Aluno a;
if (lerAlunoPorIndice("alunos.bin", total - 1, &a))
printf("Último: %d | %s | %.1f\n",
a.matricula, a.nome, a.nota);
/* Lê diretamente o primeiro */
if (lerAlunoPorIndice("alunos.bin", 0, &a))
printf("Primeiro: %d | %s | %.1f\n",
a.matricula, a.nome, a.nota);
return 0;
}
fseek pula direto para lá em uma instrução — sem varrer os 9.998 anteriores. Isso é o equivalente ao acesso direto de um array, mas em disco.
Com fseek, é possível também sobrescrever um registro específico — sem recriar o arquivo inteiro:
#include <stdio.h>
typedef struct {
int matricula;
char nome[50];
float nota;
} Aluno;
/* Atualiza o aluno na posição 'indice' sem tocar nos outros */
int atualizarAluno(const char *caminho, int indice, Aluno *novo) {
FILE *f = fopen(caminho, "rb+"); // "rb+" = leitura + escrita binária
if (f == NULL) return 0;
long offset = (long)indice * sizeof(Aluno);
fseek(f, offset, SEEK_SET);
int ok = (fwrite(novo, sizeof(Aluno), 1, f) == 1);
fclose(f);
return ok;
}
int main() {
/* Supondo que alunos.bin já existe com Ana, Bruno, Carla */
Aluno corrigido = {1002, "Bruno Lima", 7.5f}; // nota de 6.0 para 7.5
if (atualizarAluno("alunos.bin", 1, &corrigido))
printf("Nota de Bruno atualizada.\n");
return 0;
}
"rb+" abre o arquivo binário existente para leitura e escrita simultâneas, sem apagar o conteúdo. É o modo correto para atualizar registros no lugar (in-place update).
// Dados simples, configurações // Logs legíveis por humanos // Troca de dados com outros sistemas // Nomes sem espaços ou com parse manual fprintf(f, "%d %.1f\n", mat, nota);
// Structs completas com todos os campos // Nomes com espaços (funciona direto) // Acesso direto a registros por índice // Performance com grandes volumes fwrite(&aluno, sizeof(Aluno), 1, f);
| Função | Direção | Para quê |
|---|---|---|
fprintf | Escrita texto | Gravar dados formatados como string |
fscanf | Leitura texto | Ler dados formatados — para no espaço |
fgets | Leitura texto | Ler linha inteira com espaços |
fwrite | Escrita binária | Gravar struct ou array de bytes |
fread | Leitura binária | Ler struct ou array de bytes |
fseek | Posicionamento | Mover cursor para offset específico |
ftell | Posição atual | Saber quantos bytes desde o início |
rewind | Volta ao início | Atalho para fseek(f,0,SEEK_SET) |