C++: Escrita e Leitura em Arquivos

Manipulação de Dados Persistentes

Tópicos

  • Introdução à manipulação de arquivos
  • Biblioteca fstream
  • Abertura e fechamento de arquivos
  • Escrita em arquivos de texto
  • Leitura de arquivos de texto
  • Modos de abertura de arquivos
  • Verificação de erros
  • Manipulação de dados binários
  • Boas práticas

Introdução à Manipulação de Arquivos

  • Importância do armazenamento persistente de dados
  • Tipos de arquivos: texto e binário
  • Operações básicas: abertura, leitura, escrita, fechamento
  • Aplicações: logs, configurações, dados de usuário, etc.

Biblioteca fstream

  • Parte da biblioteca padrão de C++
  • Fornece classes para manipulação de arquivos
  • Principais classes:
    • ofstream: para escrita em arquivos
    • ifstream: para leitura de arquivos
    • fstream: para leitura e escrita

Abertura e Fechamento de Arquivos


#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ofstream arquivo("exemplo.txt");
    
    if (arquivo.is_open()) {
        arquivo << "Olá, mundo!" << endl;
        arquivo.close();
        cout << "Arquivo criado com sucesso." << endl;
    } else {
        cout << "Erro ao abrir o arquivo." << endl;
    }

    return 0;
}
                

Escrita em Arquivos de Texto


ofstream arquivo("dados.txt");

if (arquivo.is_open()) {
    arquivo << "Nome: João" << endl;
    arquivo << "Idade: 30" << endl;
    arquivo << "Profissão: Engenheiro" << endl;

    arquivo.close();
} else {
    cout << "Não foi possível abrir o arquivo." << endl;
}
                

Leitura Simples de Arquivo


#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ifstream arquivo("numeros.txt");
    const int MAX_NUMEROS = 100;
    int numeros[MAX_NUMEROS];
    int contador = 0;

    if (arquivo.is_open()) {
        while (arquivo >> numeros[contador] && contador < MAX_NUMEROS) {
            contador++;
        }
        arquivo.close();

        cout << "Números lidos: ";
        for (int i = 0; i < contador; i++) {
            cout << numeros[i] << " ";
        }
        cout << endl;
    } else {
        cout << "Não foi possível abrir o arquivo." << endl;
    }

    return 0;
}
                

Este exemplo lê números inteiros de um arquivo e os armazena em um array.

Modos de Abertura de Arquivos

  • ios::out - Modo padrão, sobrescreve o conteúdo
  • ios::app - Adiciona ao final do arquivo
  • ios::trunc - Trunca o arquivo se já existir
  • ios::binary - Modo binário

ofstream arquivo("log.txt", ios::app);
arquivo << "Nova entrada de log" << endl;
                

Verificação de Erros


ofstream arquivo("dados.txt");

if (!arquivo) {
    cerr << "Erro ao abrir o arquivo!" << endl;
    return 1;
}

arquivo << "Dados importantes" << endl;

if (arquivo.fail()) {
    cerr << "Erro ao escrever no arquivo!" << endl;
    arquivo.close();
    return 1;
}

arquivo.close();
                

Entendendo o reinterpret_cast

  • Usado para converter entre tipos de ponteiros incompatíveis
  • Permite tratar dados como uma sequência de bytes
  • Essencial para escrita/leitura binária de tipos complexos
  • Sintaxe: reinterpret_cast<novo_tipo>(expressão)

int numero = 42;
char* bytes = reinterpret_cast<char*>(&numero);
// Agora 'bytes' aponta para os 4 bytes que compõem 'numero'
                

Cuidado: O uso incorreto pode levar a problemas de portabilidade e segurança!

Escrita de Struct em Arquivo - Parte 1


struct Aluno {
    int id;
    char nome[50];
    float nota;
};

void escreverAlunos(const Aluno alunos[], int tamanho, const char* nomeArquivo) {
    ofstream arquivo(nomeArquivo, ios::binary);
    if (arquivo.is_open()) {
        for (int i = 0; i < tamanho; i++) {
            arquivo.write(reinterpret_cast<const char*>(&alunos[i]), sizeof(Aluno));
        }
        arquivo.close();
        cout << "Dados escritos com sucesso." << endl;
    } else {
        cout << "Erro ao abrir o arquivo para escrita." << endl;
    }
}
                

Leitura de Struct de Arquivo - Parte 2


int lerAlunos(Aluno alunos[], int maxAlunos, const char* nomeArquivo) {
    ifstream arquivo(nomeArquivo, ios::binary);
    int contador = 0;
    if (arquivo.is_open()) {
        while (arquivo.read(reinterpret_cast<char*>(&alunos[contador]), sizeof(Aluno))) {
            if (++contador >= maxAlunos) break;
        }
        arquivo.close();
        cout << "Dados lidos com sucesso." << endl;
    } else {
        cout << "Erro ao abrir o arquivo para leitura." << endl;
    }
    return contador;
}
                

Exemplo de Uso - Parte 3


int main() {
    const int MAX_ALUNOS = 3;
    Aluno alunos[MAX_ALUNOS] = {
        {1, "João", 8.5},
        {2, "Maria", 9.0},
        {3, "Pedro", 7.5}
    };

    escreverAlunos(alunos, MAX_ALUNOS, "alunos.dat");

    Aluno alunosLidos[MAX_ALUNOS];
    int numLidos = lerAlunos(alunosLidos, MAX_ALUNOS, "alunos.dat");

    for (int i = 0; i < numLidos; i++) {
        cout << "ID: " << alunosLidos[i].id 
             << ", Nome: " << alunosLidos[i].nome
             << ", Nota: " << alunosLidos[i].nota << endl;
    }

    return 0;
}
                

Boas Práticas

  • Sempre feche os arquivos após o uso
  • Verifique erros ao abrir e manipular arquivos
  • Use caminhos relativos ou absolutos com cuidado
  • Faça backup de dados importantes antes de sobrescrever
  • Use nomes de arquivos significativos e extensões apropriadas

Conclusão

  • A manipulação de arquivos é essencial para persistência de dados
  • C++ oferece ferramentas poderosas através da biblioteca fstream
  • É possível ler e escrever desde dados simples até estruturas complexas
  • O uso de reinterpret_cast é crucial para manipulação binária de dados
  • Compreender os modos de abertura e tratamento de erros é importante
  • Práticas adequadas garantem segurança e confiabilidade dos dados