Manipulação de Arquivos e Tratamento de Exceções em Python

A capacidade de ler e escrever dados em arquivos é fundamental para a persistência de informações em qualquer aplicação. Seja para guardar configurações, dados gerados pelo usuário ou informações obtidas de fontes externas, o armazenamento em disco é essencial. Python oferece mecanismos robustos para interagir com o sistema de arquivos, mas essa interação também é uma fonte comum de erros (arquivos ausentes, permissões negadas, disco cheio, etc.). Portanto, uma manipulação de arquivos segura e confiável anda de mãos dadas com um tratamento de exceções eficaz.

Manipulação Básica de Arquivos

A função primordial para trabalhar com arquivos em Python é a função nativa open(). Ela retorna um objeto de arquivo (handle) e possui vários parâmetros:


open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

Os parâmetros mais utilizados são file (o caminho para o arquivo), mode (o modo de operação) e encoding (a codificação do texto).

Modos de Operação de Arquivo:

  • 'r': Modo de leitura (padrão). O arquivo deve existir, caso contrário, uma FileNotFoundError será levantada.
  • 'w': Modo de escrita. Se o arquivo existir, seu conteúdo é apagado. Se não existir, um novo arquivo é criado.
  • 'x': Modo de criação exclusiva. Se o arquivo já existir, a operação falha com FileExistsError.
  • 'a': Modo de apêndice (append). Dados são adicionados ao final do arquivo. Se o arquivo não existir, ele é criado.
  • 'b': Modo binário. Usado em conjunto com outros modos, como 'rb' ou 'wb', para dados não textuais.
  • 't': Modo texto (padrão). Usado em conjunto com outros modos, como 'rt' ou 'wt'.
  • '+': Modo de atualização (leitura e escrita). Pode ser combinado com 'r', 'w' ou 'a' (ex: 'r+'). O ponteiro do arquivo inicia no começo. 'w+' também apaga o conteúdo, e 'a+' inicia a escrita no final.

Observações importantes:

  • Em ambientes Windows, o caractere de nova linha é \r\n, enquanto em sistemas Unix/Linux é \n. O modo texto realiza uma conversão automática. Para desativar essa conversão, use newline=''.
  • Especificar a codificação (encoding) é crucial, especialmente no modo texto. Recomenda-se o uso de 'utf-8'.

Métodos Comuns de Objetos de Arquivo:

  • read(size=-1): Lê todo o conteúdo do arquivo ou até size caracteres/bytes. Evite usar para arquivos muito grandes.
  • readline(size=-1): Lê uma única linha do arquivo, incluindo o caractere de nova linha.
  • readlines(hint=-1): Lê todas as linhas e retorna uma lista.
  • write(s): Escreve a string s (modo texto) ou o objeto de bytes (modo binário) no arquivo.
  • writelines(lines): Escreve uma lista de strings (ou objetos de bytes) no arquivo. Note que não adiciona novas linhas automaticamente.
  • tell(): Retorna a posição atual do ponteiro do arquivo.
  • seek(offset, whence=0): Move o ponteiro do arquivo. whence pode ser 0 (início do arquivo, padrão), 1 (posição atual) ou 2 (fim do arquivo).
  • close(): Fecha o arquivo. Essencial para liberar recursos e garantir que os dados sejam gravados.

Gerenciamento de Contexto e Tratamento de Exceções

Por que Fechar Arquivos é Crucial?

Manter arquivos abertos consome recursos do sistema operacional. O não fechamento pode levar a vazamento de recursos e falhas. Além disso, dados em buffers podem não ser efetivamente escritos em disco até o fechamento.

A abordagem tradicional envolve try...finally:


arquivo_handle = open('dados.txt', 'r')
try:
    conteudo = arquivo_handle.read()
    # Processar conteudo
finally:
    arquivo_handle.close() # Garante o fechamento mesmo com erros

O Uso Elegante do with

A instrução with, que utiliza gerenciadores de contexto, simplifica e garante o fechamento automático do arquivo, mesmo na ocorrência de exceções.


with open('dados.txt', 'r', encoding='utf-8') as f:
    conteudo = f.read()
# O arquivo 'f' é fechado automaticamente aqui

Tratando Exceções Comuns em Operações de Arquivo:

  • FileNotFoundError: O arquivo não foi encontrado.
  • PermissionError: Falta de permissão para acessar o arquivo.
  • IsADirectoryError: Tentativa de abrir um diretório como um arquivo.
  • IOError (ou subclasses): Erros gerais de entrada/saída (ex: disco cheio).

É essencial capturar essas exceções para fornecer feedback adequado ao usuário ou para implementar lógicas de recuperação.


import sys

nome_arquivo = 'config.ini'
try:
    with open(nome_arquivo, 'r', encoding='utf-8') as f:
        config_data = f.read()
        # processar config_data
except FileNotFoundError:
    print(f"Erro: O arquivo '{nome_arquivo}' não foi encontrado.", file=sys.stderr)
except PermissionError:
    print(f"Erro: Sem permissão para ler o arquivo '{nome_arquivo}'.", file=sys.stderr)
except Exception as e:
    print(f"Um erro inesperado ocorreu ao ler o arquivo: {e}", file=sys.stderr)

Técnicas Avançadas

Processamento de Arquivos Grandes

Para arquivos que não cabem na memória, a leitura linha a linha ou em blocos é a solução:


# Leitura linha a linha (recomendado para texto)
with open('log_grande.txt', 'r', encoding='utf-8') as f:
    for linha in f: # O objeto de arquivo é iterável
        processar_linha(linha)

# Leitura em blocos (útil para binários)
tamanho_bloco = 1024 * 1024 # 1MB
with open('arquivo_binario_grande.dat', 'rb') as f:
    while True:
        bloco = f.read(tamanho_bloco)
        if not bloco:
            break
        processar_bloco(bloco)

Acesso Aleatório com seek() e tell()

Arquivos binários suportam acesso aleatório eficientemente. O acesso aleatório em arquivos de texto pode ser impreciso devido às conversões de caracteres de nova linha.


with open('dados_aleatorios.bin', 'rb') as f:
    primeiro_trecho = f.read(16)
    posicao_atual = f.tell() # Salva a posição
    f.seek(100, 1) # Avança 100 bytes a partir da posição atual
    segundo_trecho = f.read(16)
    f.seek(posicao_atual) # Retorna à posição salva
    terceiro_trecho = f.read(16)

Uso do Módulo tempfile

Para arquivos temporários que devem ser automaticamente removidos após o uso:


import tempfile

# Cria um arquivo temporário que será deletado ao sair do bloco 'with'
with tempfile.NamedTemporaryFile(mode='w+', delete=True, suffix='.tmp', encoding='utf-8') as temp_f:
    temp_f.write("Dados temporários importantes.\n")
    temp_f.flush() # Garante que os dados sejam escritos
    temp_f.seek(0)
    conteudo_temp = temp_f.read()
    # Arquivo é removido automaticamente aqui

Exemplo Prático: Um Sistema Simples de Gerenciamento de Tarefas

Vamos criar um aplicativo de linha de comando para gerenciar tarefas, salvando-as em um arquivo CSV.


import csv
import os
from typing import List, Dict, Optional

ARQUIVO_TAREFAS = 'tarefas.csv'

def carregar_tarefas() -> List[Dict[str, str]]:
    """Carrega tarefas do arquivo CSV."""
    tarefas = []
    if not os.path.exists(ARQUIVO_TAREFAS):
        return tarefas
    
    try:
        with open(ARQUIVO_TAREFAS, 'r', newline='', encoding='utf-8') as f:
            leitor = csv.DictReader(f)
            for linha in leitor:
                tarefas.append(linha)
    except Exception as e:
        print(f"Falha ao carregar tarefas: {e}", file=sys.stderr)
    return tarefas

def salvar_tarefas(tarefas: List[Dict[str, str]]):
    """Salva a lista de tarefas no arquivo CSV."""
    try:
        with open(ARQUIVO_TAREFAS, 'w', newline='', encoding='utf-8') as f:
            nomes_colunas = ['descricao', 'status']
            escritor = csv.DictWriter(f, fieldnames=nomes_colunas)
            escritor.writeheader()
            escritor.writerows(tarefas)
    except Exception as e:
        print(f"Falha ao salvar tarefas: {e}", file=sys.stderr)

def adicionar_tarefa(tarefas: List[Dict[str, str]], descricao: str):
    """Adiciona uma nova tarefa."""
    if not descricao:
        print("Descrição da tarefa não pode ser vazia.")
        return
    tarefas.append({'descricao': descricao, 'status': 'pendente'})
    salvar_tarefas(tarefas)
    print(f"Tarefa '{descricao}' adicionada.")

def marcar_como_concluida(tarefas: List[Dict[str, str]], indice: int):
    """Marca uma tarefa como concluída."""
    if 0 <= indice < len(tarefas):
        tarefas[indice]['status'] = 'concluida'
        salvar_tarefas(tarefas)
        print(f"Tarefa '{tarefas[indice]['descricao']}' marcada como concluída.")
    else:
        print("Índice de tarefa inválido.")

def listar_tarefas(tarefas: List[Dict[str, str]]):
    """Lista todas as tarefas."""
    if not tarefas:
        print("Nenhuma tarefa cadastrada.")
        return
    print("\n--- Lista de Tarefas ---")
    for i, tarefa in enumerate(tarefas):
        print(f"{i}. [{tarefa['status']}] {tarefa['descricao']}")
    print("----------------------")

def main():
    lista_de_tarefas = carregar_tarefas()
    
    while True:
        print("\nGerenciador de Tarefas Simples")
        print("1. Adicionar Tarefa")
        print("2. Listar Tarefas")
        print("3. Marcar como Concluída")
        print("4. Sair")
        
        escolha = input("Escolha uma opção: ").strip()
        
        if escolha == '1':
            desc = input("Digite a descrição da tarefa: ").strip()
            adicionar_tarefa(lista_de_tarefas, desc)
        elif escolha == '2':
            listar_tarefas(lista_de_tarefas)
        elif escolha == '3':
            listar_tarefas(lista_de_tarefas)
            try:
                idx = int(input("Digite o número da tarefa a marcar como concluída: ").strip())
                marcar_como_concluida(lista_de_tarefas, idx)
            except ValueError:
                print("Entrada inválida. Por favor, digite um número.")
            except Exception as e:
                 print(f"Ocorreu um erro: {e}", file=sys.stderr)
        elif escolha == '4':
            print("Saindo...")
            break
        else:
            print("Opção inválida. Tente novamente.")

if __name__ == '__main__':
    main()

Este exemplo demonstra:

  1. Persistência de Dados: Tarefas são salvas em um arquivo CSV.
  2. Tratamento de Erros na Leitura: os.path.exists previne FileNotFoundError inicial. O bloco try...except captura outros erros durante a leitura.
  3. Tratamento de Erros na Escrita: O bloco try...except na função salvar_tarefas lida com falhas de escrita.
  4. Codificação: encoding='utf-8' é usado para compatibilidade.
  5. Gerenciamento de Contexto: with open(...) garante o fechamento seguro dos arquivos.
  6. Validação de Entrada: Tratamento de ValueError ao converter entrada para índice numérico.

Tags: Python manipulação de arquivos Tratamento de Exceções entrada e saída gerenciamento de contexto

Publicado em 6-10 01:19 por Thomas