Arquivos: fopen, fread, fwrite, fclose
1. Introdução à Manipulação de Arquivos em C
Na linguagem C, arquivos são tratados como fluxos de bytes (streams), independentemente do tipo de dado armazenado. A biblioteca padrão stdio.h fornece um conjunto de funções que permitem abrir, ler, escrever e fechar arquivos de forma eficiente. Antes de explorar as funções específicas, é fundamental entender os dois modos de acesso principais:
- Modo texto: Os dados são interpretados como caracteres. O sistema pode realizar conversões automáticas (como
\npara\r\nno Windows). - Modo binário: Os dados são lidos e escritos exatamente como estão na memória, sem qualquer conversão. Indicado para structs, arrays e dados não textuais.
As funções fopen, fclose, fwrite e fread são as ferramentas essenciais para manipulação binária de arquivos, oferecendo controle direto sobre a leitura e escrita de blocos de dados.
2. A Função fopen: Abrindo Arquivos
A função fopen é responsável por estabelecer uma conexão entre o programa e um arquivo no sistema. Seu protótipo é:
FILE *fopen(const char *filename, const char *mode);
O primeiro parâmetro é o nome do arquivo (pode incluir caminho). O segundo parâmetro define o modo de abertura. Os modos mais comuns são:
| Modo | Descrição |
|---|---|
"r" |
Leitura (texto). O arquivo deve existir. |
"w" |
Escrita (texto). Cria ou sobrescreve. |
"a" |
Anexar (texto). Cria se não existir. |
"rb" |
Leitura binária. |
"wb" |
Escrita binária. |
"ab" |
Anexar binário. |
"r+" |
Leitura e escrita (texto). |
"w+" |
Leitura e escrita (texto). Cria/sobrescreve. |
"a+" |
Leitura e anexar (texto). |
O tratamento de erros é crucial: se fopen falhar, retorna NULL. Devemos sempre verificar esse retorno:
FILE *arquivo = fopen("dados.bin", "rb");
if (arquivo == NULL) {
perror("Erro ao abrir arquivo");
return 1;
}
A função perror exibe uma mensagem descritiva baseada na variável global errno.
3. A Função fclose: Fechando Arquivos
Todo arquivo aberto deve ser fechado para liberar recursos do sistema e garantir que os buffers sejam descarregados. O protótipo é:
int fclose(FILE *stream);
Retorna 0 em caso de sucesso ou EOF (geralmente -1) em caso de erro. Exemplo:
if (fclose(arquivo) == EOF) {
perror("Erro ao fechar arquivo");
}
Ignorar o fechamento pode causar vazamento de memória, perda de dados (buffers não escritos) e impedir que outros processos acessem o arquivo. Sempre feche arquivos assim que terminar de usá-los.
4. A Função fwrite: Escrevendo Dados Binários
fwrite permite escrever blocos de dados binários diretamente em um arquivo. Seu protótipo:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr: ponteiro para os dados a serem escritossize: tamanho de cada elemento (em bytes)nmemb: número de elementosstream: ponteiro para o arquivo
Retorna o número de elementos efetivamente escritos. Exemplo com array:
int numeros[] = {10, 20, 30, 40, 50};
FILE *arquivo = fopen("numeros.bin", "wb");
if (arquivo != NULL) {
size_t escritos = fwrite(numeros, sizeof(int), 5, arquivo);
if (escritos != 5) {
perror("Erro ao escrever");
}
fclose(arquivo);
}
Para structs, o uso é igualmente direto:
struct Pessoa {
char nome[50];
int idade;
float altura;
};
struct Pessoa p = {"Maria", 25, 1.68};
fwrite(&p, sizeof(struct Pessoa), 1, arquivo);
5. A Função fread: Lendo Dados Binários
fread é a contraparte de fwrite, lendo blocos binários de um arquivo. Protótipo:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr: buffer de destinosize: tamanho de cada elementonmemb: número de elementos a lerstream: ponteiro para o arquivo
Retorna o número de elementos lidos. Se for menor que nmemb, pode indicar fim de arquivo ou erro. Exemplo:
int buffer[5];
FILE *arquivo = fopen("numeros.bin", "rb");
if (arquivo != NULL) {
size_t lidos = fread(buffer, sizeof(int), 5, arquivo);
if (lidos == 0 && feof(arquivo)) {
printf("Arquivo vazio ou fim atingido.\n");
} else if (lidos != 5) {
perror("Erro de leitura");
}
fclose(arquivo);
}
A função feof pode ser usada para verificar se o fim do arquivo foi atingido, enquanto ferror verifica erros de leitura.
6. Exemplo Prático Integrado: Cadastro em Arquivo Binário
Vamos criar um sistema simples de cadastro de alunos usando arquivo binário. Primeiro, definimos a estrutura:
#include <stdio.h>
#include <string.h>
#define MAX_ALUNOS 3
struct Aluno {
int matricula;
char nome[50];
float nota;
};
Escrita dos registros:
void escreverAlunos() {
struct Aluno alunos[MAX_ALUNOS] = {
{101, "Ana Silva", 8.5},
{102, "Carlos Souza", 7.2},
{103, "Beatriz Lima", 9.1}
};
FILE *arquivo = fopen("alunos.bin", "wb");
if (arquivo == NULL) {
perror("Erro ao criar arquivo");
return;
}
size_t escritos = fwrite(alunos, sizeof(struct Aluno), MAX_ALUNOS, arquivo);
if (escritos != MAX_ALUNOS) {
perror("Erro ao escrever registros");
}
fclose(arquivo);
printf("Dados gravados com sucesso!\n");
}
Leitura dos registros:
void lerAlunos() {
struct Aluno aluno;
FILE *arquivo = fopen("alunos.bin", "rb");
if (arquivo == NULL) {
perror("Erro ao abrir arquivo");
return;
}
printf("\n--- Registros dos Alunos ---\n");
while (fread(&aluno, sizeof(struct Aluno), 1, arquivo) == 1) {
printf("Matrícula: %d | Nome: %s | Nota: %.2f\n",
aluno.matricula, aluno.nome, aluno.nota);
}
if (feof(arquivo)) {
printf("\nFim do arquivo atingido.\n");
} else if (ferror(arquivo)) {
perror("Erro durante a leitura");
}
fclose(arquivo);
}
Função principal:
int main() {
escreverAlunos();
lerAlunos();
return 0;
}
Ao executar, o programa cria o arquivo alunos.bin, grava três registros e os lê de volta, exibindo os dados na tela. Note como fread é usado em um loop até que retorne 0 (indicando fim do arquivo ou erro).
7. Boas Práticas e Erros Comuns
- Sempre verifique o retorno de
fopen: Usar um ponteiroNULLcausa falha de segmentação. - Feche arquivos imediatamente: Esquecer
fclosepode travar o arquivo para outros processos e causar perda de dados. - Cuidado com padding de structs: O compilador pode adicionar bytes de alinhamento entre os campos de uma struct. Isso pode tornar arquivos incompatíveis entre diferentes compiladores ou plataformas. Para evitar problemas, use
#pragma pack(1)ou serialize manualmente os campos. - Prefira
fwrite/freadpara dados binários: Funções comofprintf/fscanfconvertem dados para texto, o que é mais lento e pode perder precisão em números de ponto flutuante. - Use
feofeferrorapósfread: O retorno defreadsozinho não distingue entre fim de arquivo e erro. Sempre combine com essas funções para diagnóstico correto. - Não assuma que
fwriteescreverá todos os elementos: Em caso de erro (disco cheio, por exemplo), o retorno será menor que o solicitado. Verifique sempre.
Dominar essas quatro funções é essencial para qualquer programador C que precise trabalhar com persistência de dados binários, sejam eles structs, arrays ou qualquer outro tipo de dado bruto.
Referências
- fopen, fopen_s - cppreference.com — Documentação oficial detalhada sobre a função fopen, incluindo todos os modos de abertura e exemplos.
- fclose - cppreference.com — Referência completa para a função fclose, com discussão sobre buffers e valores de retorno.
- fread - cppreference.com — Documentação oficial da função fread, incluindo tratamento de fim de arquivo e erros.
- fwrite - cppreference.com — Referência para fwrite com exemplos de escrita de arrays e estruturas.
- Binary File I/O in C (GeeksforGeeks) — Tutorial prático sobre leitura e escrita binária em C, com exemplos de structs e detecção de erros.
- C Programming - File Handling (TutorialsPoint) — Guia completo sobre manipulação de arquivos em C, cobrindo fopen, fclose, fread, fwrite e outros.
- Using fread and fwrite in C (Programiz) — Exemplos didáticos de uso de fread e fwrite com arrays e estruturas, incluindo verificação de retorno.