A manipulação de dados perssitentes envolve operações de leitura (transferência do arquivo para o ambiente do programa) e escrita (saída do programa para o armazenamento externo). A biblioteca padrão do C++ abstrai essas operações complexas através de classes que encapsulam a lógica de interação com o sistema operacional.
Bibliotecas Essenciais
| Livro de Cabeçalho | Propósito Principle |
|---|---|
| <fstream> | Define as classes principais de arquivos como fstream, ifstream e ofstream. |
| <ios> | Contém definições de estado do fluxo e modos de abertura enumerados. |
| <iostream> | Fornece os fluxos padrão console (cin, cout) que compartilham sintaxe com arquivos. |
| <streambuf> | Gerencia a lógica do buffer de memória utilizado nos fluxos. |
| <sstream> | Implementa fluxos de memória interna (strings) para processamento intermediário. |
Principais Classes de Fluxo
Três classes formam a base da programação orientada a objetos para arquivos, todas derivadas de um mesmo hierarquia:
- ifstream: Focado exclusivamente em leituras (Input).
- ofstream: Projetado para gravações (Output).
- fstream: Suporta operações bidirecionais (Leitura e Escrita simultâneas).
O primeiro parâmetro nas construtoras refere-se ao caminho do arquivo (absoluto ou relativo), seguido opcionalmente pelos modos de abertura definidos em ios_base.
Modos de Abertura Comuns
As constantes de modo podem ser combinadas usando o operador bitwise OR (|):
| Constante | Descrição | Cenário Típico |
|---|---|---|
ios::in |
Aberto apenas para ler. | ifstream ou fstream |
ios::out |
Aberto para escrever (limpa conteúdo existente por padrão). | ofstream ou fstream |
ios::app |
Posiciona o cursor ao final do arquivo antes de qualquer escrita. | Anotações diárias em log. |
ios::binary |
Trata o arquivo como bytes brutos, ignorando conversões de plataforma. | Gráficos, sons, arquivos executáveis. |
ios::trunc |
Apaga o conteúdo atual se o arquivo já existir. | Resetar arquivos de configuração. |
ios::ate |
Inicializa o cursor no final do arquivo imediatamente após abrir. | Append manual personalizado. |
Exemplos práticos de combinação:
- Leitura simples:
ios::in - Sobrescrição total:
ios::out | ios::trunc - Adição ao fim:
ios::app - Dados binários puros:
ios::in | ios::binary
Estrutura da Hierarquia de Classes
Compreender a árvore de herança ajuda a entender quais métodos estão disponíveis:
ios_base (Base: Definição de estados e flags)
|
ios (Interface base de fluxos)
|-- istream (Leitura básica, herdado por cin)
| |-- ifstream (Leitura de arquivos)
| |-- istringstream (Leitura de String)
|
|-- iostream (Leitura e Escrita combinadas)
|
|-- fstream (Fluxo completo de arquivos)
|-- stringstream (Fluxo completo de String)
|
|-- ostream (Escrita básica, herdado por cout)
|-- ofstream (Escrita de arquivos)
|-- ostringstream (Escrita para String)
Gerenciamento de Buffer
Por desempenho, os dados não vão direto para o disco físico. Eles ficam armazenados temporariamente em um buffer. Para garantir que os dados sejam efetivamente gravados ou lidos, deve-se chamar o método flush() explicitamente ou usar endl (que insere uma nova linha e força o flush). O fechamento automático do objeto via close() também executa essa limpeza.
Operações de Leitura
Instanciando o Leitor
Existem duas abordagens padrão para inicializar o objeto de entrada:
- Construção Direta: Abre o arquivo imediatamente na instanciação.
#include <fstream>
#include <iostream>
int main() {
// Tenta abrir 'config.cfg' automaticamente no modo de leitura
std::ifstream entrada("config.cfg");
return 0;
}
- Abertura Sepaarda: Cria o objeto vazia e define o destino depois.
std::ifstream leitorDeArq;
leitorDeArq.open("log.txt"); // Pode ser chamado quantas vezes necessário (com close prévio)
Métodos de Extração
Diferentes cenários exigem diferentes formas de capturar o conteúdo:
Lendo Linhas Completas
Utilizado quando o separador é definido pela troca de linha (\n):
char linha[256];
// Loop enquanto houver conteúdo válido na entrada
while (leitorDeArq.getline(linha, 256)) {
std::cout << "Lido: " << linha << std::endl;
}
// Nota: getline retorna false ao encontrar falha ou EOF
Lendo Tokens (Palavras)
Igual ao uso do cin, ignora espaços em branco e quebras de linha até encontrar um dado:
char palavra[256];
while (!leitorDeArq.eof()) {
leitorDeArq >> palavra; // Pula espaços em branco automaticamente
std::cout << palavra << std::endl;
}
Técnicas Avançadas
Verificação de Estado: Antes de tentar ler, verifique se o arquivo foi acessado corretamente.
std::ifstream arquivo("usuario.csv");
if (!arquivo.is_open()) {
std::cerr << "Erro ao acessar 'usuario.csv'" << std::endl;
return 1;
}
Leitura Binária: Necessária para formatos estruturados ou não-textuais.
// Habilitar modo binário explícito
std::ifstream raw("dados.dat", std::ios::binary | std::ios::in);
float valor;
// Leitura direta de memória
raw.read(reinterpret_cast<char>(&valor), sizeof(float));
// Verifica se a quantidade de bytes lida corresponde ao esperado
if (raw.gcount() == sizeof(float)) {
std::cout << "Valor bruto: " << valor << std::endl;
}</char>
Status da Stream: Substitua good() por checks mais específicos:
eof(): Chegamos ao fim do registro.fail(): Erro de formato ou tipo (ex: tentar ler texto em um int).bad(): Erro de dispositivo ou corrupção grave.
Operações de Escrita
Configuração do Escritor
Para evitar perda de dados acidental ao reutilizar arquivos, utilize o modo append:
std::ofstream saida("relatorio.txt", std::ios::app | std::ios::out);
if (!saida) {
std::cerr << "Não foi possível abrir para escrita." << std::endl;
return 1;
}
Para sobrescrever tudo, omita o app (ou adicione trunc explicitamente):
std::ofstream novoArq("output.txt", std::ios::out);
Formatos de Gravação
Método Stream (<<)
Ideal para textos legíveis e formatação automatizada:
saida << "Usuário iniciante" << std::endl;
saida << "Status: Ativo" << "\n";
Método Binário (write)
Necessário para serialização direta de structs ou primitivos:
int pontuacao = 1000;
saida.write(reinterpret_cast<const char="">(&pontuacao), sizeof(int));</const>
Gestão de Recursos
Embora destrutores fechem arquivos automaticamente, saida.close() é recomendado após blocos críticos para liberar locks do sistema operacional imediatamente. Além disso, lembre-se que ios::app impede movimento interno do cursor (seekp); sempre será escrito no fim.
Estudo de Caso: Gerenciamento de Entidades
Abaixo, um exemplo consolidando leitura, modelagem de dados e estrutura de lista encadeada.
Definição da Classe Pessoa
Representação simples de um dado complexo contido em arquivo:
#include <string>
#include <vector>
struct DadoPessoa {
std::string nome;
std::vector<std::string> habilidades;
int atributoPrincipal;
};
Processamento e Armazenamento
Leitura sequencial e construção dinâmica da coleção em memória:
#include <list>
#include <fstream>
int main() {
std::list<DadoPessoa> colecao;
// Abertura do banco de dados
std::ifstream db("base_personas.dat", std::ios::binary);
if (!db) return -1;
DadoPessoa entidade;
// Loop de deserialização (assumindo formato conhecido no arquivo)
while (db.read(reinterpret_cast<char>(&entidade), sizeof(DadoPessoa))) {
colecao.push_back(entidade);
}
// Iteração sobre os dados carregados
for (auto &it : colecao) {
std::cout << "ID: " << it.atributoPrincipal << " | Nome: " << it.nome << std::endl;
}
return 0;
}</char>