Leitura e escrita de arquivos binários
1. Introdução aos arquivos binários
1.1 Diferença entre arquivos texto e binários
Arquivos texto armazenam dados como caracteres legíveis, usando codificações como ASCII ou UTF-8. Cada número ou valor é convertido em sua representação textual antes de ser gravado. Já arquivos binários armazenam dados exatamente como estão na memória do computador, sem qualquer conversão ou formatação. Enquanto um arquivo texto com o inteiro 12345 ocupa 5 bytes (um para cada caractere), um arquivo binário ocupa apenas 4 bytes (tamanho de um int).
1.2 Quando usar arquivos binários
Arquivos binários são ideais quando:
- Eficiência: leitura e escrita mais rápidas, sem conversões
- Precisão: números de ponto flutuante não sofrem perda por arredondamento em conversões texto
- Dados estruturados: structs e arrays podem ser gravados e lidos em bloco
- Tamanho reduzido: dados ocupam menos espaço em disco
1.3 Modos de abertura
| Modo | Descrição |
|---|---|
"rb" |
Leitura binária |
"wb" |
Escrita binária (cria/sobrescreve) |
"ab" |
Anexação binária (adiciona ao final) |
"rb+" |
Leitura e escrita binária |
"wb+" |
Leitura e escrita (cria/sobrescreve) |
2. Abertura e fechamento de arquivos binários
2.1 Função fopen() para arquivos binários
FILE *arquivo = fopen("dados.bin", "wb");
2.2 Tratamento de erros na abertura
FILE *arquivo = fopen("dados.bin", "rb");
if (arquivo == NULL) {
perror("Erro ao abrir arquivo");
return 1;
}
2.3 Função fclose() e boas práticas
if (fclose(arquivo) != 0) {
perror("Erro ao fechar arquivo");
}
Sempre feche arquivos após o uso para liberar recursos e evitar corrupção de dados.
3. Escrita de dados binários com fwrite()
3.1 Sintaxe e parâmetros
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
ptr: ponteiro para os dados a serem escritossize: tamanho de cada elemento em bytescount: número de elementosstream: ponteiro para o arquivo
3.2 Exemplo: escrever structs inteiras
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char nome[50];
float salario;
} Funcionario;
int main() {
Funcionario f1 = {1, "Maria Silva", 4500.50};
FILE *arquivo = fopen("funcionarios.bin", "wb");
if (arquivo == NULL) {
perror("Erro ao abrir arquivo");
return 1;
}
size_t escritos = fwrite(&f1, sizeof(Funcionario), 1, arquivo);
if (escritos != 1) {
perror("Erro ao escrever dados");
}
fclose(arquivo);
return 0;
}
3.3 Cuidados com alinhamento e padding de structs
O compilador pode adicionar bytes de preenchimento (padding) entre os membros de uma struct para alinhamento de memória. Isso pode causar incompatibilidade ao ler arquivos entre diferentes compilações ou arquiteturas.
// Exemplo de padding
typedef struct {
char letra; // 1 byte
int numero; // 4 bytes (pode ter 3 bytes de padding após char)
} Exemplo;
printf("Tamanho da struct: %zu bytes\n", sizeof(Exemplo)); // Pode mostrar 8
Para evitar problemas, use #pragma pack(1) ou declare membros em ordem decrescente de tamanho.
4. Leitura de dados binários com fread()
4.1 Sintaxe e parâmetros
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
4.2 Exemplo: ler structs de um arquivo binário
#include <stdio.h>
typedef struct {
int id;
char nome[50];
float salario;
} Funcionario;
int main() {
Funcionario f;
FILE *arquivo = fopen("funcionarios.bin", "rb");
if (arquivo == NULL) {
perror("Erro ao abrir arquivo");
return 1;
}
size_t lidos = fread(&f, sizeof(Funcionario), 1, arquivo);
if (lidos == 1) {
printf("ID: %d\n", f.id);
printf("Nome: %s\n", f.nome);
printf("Salário: %.2f\n", f.salario);
}
fclose(arquivo);
return 0;
}
4.3 Verificação do valor de retorno
O valor retornado por fread() indica quantos elementos foram lidos com sucesso. Se for menor que count, pode indicar erro ou fim de arquivo.
size_t lidos = fread(&buffer, sizeof(int), 10, arquivo);
if (lidos < 10) {
if (feof(arquivo)) {
printf("Fim do arquivo atingido. Lidos: %zu\n", lidos);
} else if (ferror(arquivo)) {
perror("Erro de leitura");
}
}
5. Posicionamento em arquivos binários
5.1 Função fseek()
int fseek(FILE *stream, long offset, int origem);
Origens disponíveis:
- SEEK_SET: início do arquivo
- SEEK_CUR: posição atual
- SEEK_END: final do arquivo
5.2 Função ftell() para obter a posição atual
long posicao = ftell(arquivo);
printf("Posição atual: %ld\n", posicao);
5.3 Acesso aleatório: lendo e escrevendo em posições específicas
// Pular para o terceiro registro (índice 2)
fseek(arquivo, 2 * sizeof(Funcionario), SEEK_SET);
// Ler o registro na posição atual
fread(&f, sizeof(Funcionario), 1, arquivo);
// Voltar 1 registro a partir da posição atual
fseek(arquivo, -sizeof(Funcionario), SEEK_CUR);
6. Tratamento de erros e fim de arquivo
6.1 Uso de feof() para detectar fim do arquivo
while (!feof(arquivo)) {
size_t lidos = fread(&f, sizeof(Funcionario), 1, arquivo);
if (lidos == 1) {
// Processar registro
}
}
6.2 Uso de ferror() para verificar erros de I/O
if (ferror(arquivo)) {
printf("Erro de I/O detectado\n");
clearerr(arquivo); // Limpa o indicador de erro
}
6.3 Limpeza do buffer de erros com clearerr()
clearerr(arquivo); // Reseta os indicadores de erro e EOF
7. Exemplo prático completo
7.1 Criação de uma struct Aluno
#include <stdio.h>
#include <string.h>
typedef struct {
char nome[50];
int idade;
float notas[3];
} Aluno;
7.2 Escrita de um vetor de alunos em arquivo binário
void escrever_alunos() {
Aluno alunos[3] = {
{"João Santos", 20, {8.5, 7.0, 9.2}},
{"Ana Oliveira", 22, {9.0, 8.5, 7.8}},
{"Carlos Lima", 19, {6.5, 7.5, 8.0}}
};
FILE *arquivo = fopen("alunos.bin", "wb");
if (arquivo == NULL) {
perror("Erro ao criar arquivo");
return;
}
fwrite(alunos, sizeof(Aluno), 3, arquivo);
fclose(arquivo);
printf("Dados escritos com sucesso!\n");
}
7.3 Leitura e exibição dos dados armazenados
void ler_alunos() {
Aluno aluno;
FILE *arquivo = fopen("alunos.bin", "rb");
if (arquivo == NULL) {
perror("Erro ao abrir arquivo");
return;
}
printf("\n=== Lista de Alunos ===\n");
int contador = 1;
while (fread(&aluno, sizeof(Aluno), 1, arquivo) == 1) {
printf("\nAluno %d:\n", contador++);
printf("Nome: %s\n", aluno.nome);
printf("Idade: %d\n", aluno.idade);
printf("Notas: %.1f, %.1f, %.1f\n",
aluno.notas[0], aluno.notas[1], aluno.notas[2]);
}
fclose(arquivo);
}
7.4 Modificação de um registro específico usando fseek()
void atualizar_aluno(int indice, Aluno novo_dado) {
FILE *arquivo = fopen("alunos.bin", "rb+");
if (arquivo == NULL) {
perror("Erro ao abrir arquivo");
return;
}
// Posicionar no registro desejado
fseek(arquivo, indice * sizeof(Aluno), SEEK_SET);
// Escrever o novo registro
fwrite(&novo_dado, sizeof(Aluno), 1, arquivo);
fclose(arquivo);
printf("Registro %d atualizado!\n", indice);
}
int main() {
escrever_alunos();
ler_alunos();
// Atualizar o segundo aluno (índice 1)
Aluno atualizado = {"Pedro Alves", 21, {10.0, 9.5, 9.8}};
atualizar_aluno(1, atualizado);
printf("\n=== Após atualização ===\n");
ler_alunos();
return 0;
}
Referências
- Documentação oficial da função fopen() - cppreference.com — Referência completa sobre modos de abertura e comportamento de arquivos em C
- Tutorial de arquivos binários em C - GeeksforGeeks — Explicação detalhada com exemplos de fread() e fwrite()
- Manipulação de arquivos binários - IBM Documentation — Guia oficial IBM sobre leitura e escrita binária em C
- Guia prático de fseek() e ftell() - TutorialsPoint — Exemplos de posicionamento aleatório em arquivos binários
- Tratamento de erros em arquivos C - Programiz — Tutorial completo com feof(), ferror() e clearerr()
- Alinhamento e padding de structs - Embedded.com — Artigo técnico sobre alinhamento de dados em structs e impactos em arquivos binários