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, umaFileNotFoundErrorserá 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 comFileExistsError.'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, usenewline=''. - 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ésizecaracteres/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 strings(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.whencepode 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:
- Persistência de Dados: Tarefas são salvas em um arquivo CSV.
- Tratamento de Erros na Leitura:
os.path.existsprevineFileNotFoundErrorinicial. O blocotry...exceptcaptura outros erros durante a leitura. - Tratamento de Erros na Escrita: O bloco
try...exceptna funçãosalvar_tarefaslida com falhas de escrita. - Codificação:
encoding='utf-8'é usado para compatibilidade. - Gerenciamento de Contexto:
with open(...)garante o fechamento seguro dos arquivos. - Validação de Entrada: Tratamento de
ValueErrorao converter entrada para índice numérico.