Manipulação de Arquivos
Introdução
A manipulação de arquivos é uma habilidade fundamental em programação, permitindo que aplicações salvem dados persistentemente, processem informações externas e compartilhem resultados. Python oferece ferramentas robustas e intuitivas para trabalhar com diversos formatos de arquivos.
Objetivos de Aprendizado
- Entender os conceitos básicos de manipulação de arquivos
- Aprender a ler e escrever arquivos de texto
- Trabalhar com formatos estruturados como JSON e CSV
- Manipular arquivos binários
- Gerenciar caminhos de arquivo com o módulo
os.path
- Aplicar boas práticas no trabalho com arquivos
Conceitos Básicos
# Método tradicional
arquivo = open("dados.txt", "r") # Modo de leitura
conteudo = arquivo.read()
arquivo.close() # Importante: sempre feche os arquivos!
# Método recomendado: using context manager (with)
with open("dados.txt", "r") as arquivo:
conteudo = arquivo.read()
# O arquivo é fechado automaticamente ao sair do bloco with
# Principais modos de abertura
# "r" - Leitura (padrão). Arquivo deve existir.
with open("arquivo.txt", "r") as f:
dados = f.read()
# "w" - Escrita. Cria arquivo novo ou sobrescreve existente.
with open("arquivo.txt", "w") as f:
f.write("Olá, mundo!")
# "a" - Append (anexar). Adiciona ao final do arquivo.
with open("arquivo.txt", "a") as f:
f.write("\nNova linha.")
# "x" - Criação exclusiva. Falha se o arquivo já existir.
with open("novo_arquivo.txt", "x") as f:
f.write("Arquivo novo")
# "b" - Modo binário (usado junto com outros modos)
with open("imagem.jpg", "rb") as f:
dados_binarios = f.read()
# "t" - Modo texto (padrão, usado junto com outros modos)
with open("arquivo.txt", "rt") as f:
texto = f.read()
# "+" - Atualização (leitura e escrita)
with open("arquivo.txt", "r+") as f:
texto = f.read()
f.write("Nova informação")
# Especificando o encoding (importante para caracteres não-ASCII)
# UTF-8 (recomendado para compatibilidade)
with open("arquivo.txt", "r", encoding="utf-8") as f:
texto = f.read()
# Latin-1 (ISO-8859-1)
with open("arquivo_latin1.txt", "r", encoding="latin-1") as f:
texto = f.read()
# Tratando erros de encoding
with open("arquivo_problematico.txt", "r", encoding="utf-8", errors="ignore") as f:
texto = f.read() # Ignora caracteres inválidos
with open("arquivo_problematico.txt", "r", encoding="utf-8", errors="replace") as f:
texto = f.read() # Substitui caracteres inválidos por '�'
Trabalhando com Arquivos de Texto
# Lendo arquivo inteiro
with open("exemplo.txt", "r", encoding="utf-8") as arquivo:
conteudo = arquivo.read()
print(conteudo)
# Lendo linha por linha
with open("exemplo.txt", "r", encoding="utf-8") as arquivo:
for linha in arquivo:
print(linha.strip()) # strip() remove espaços e quebras de linha extras
# Lendo todas as linhas em uma lista
with open("exemplo.txt", "r", encoding="utf-8") as arquivo:
linhas = arquivo.readlines()
print(f"O arquivo tem {len(linhas)} linhas")
for i, linha in enumerate(linhas):
print(f"Linha {i+1}: {linha.strip()}")
# Lendo um número específico de caracteres
with open("exemplo.txt", "r", encoding="utf-8") as arquivo:
inicio = arquivo.read(10) # Primeiros 10 caracteres
print(f"Início do arquivo: {inicio}")
# Lendo linha por linha com readline()
with open("exemplo.txt", "r", encoding="utf-8") as arquivo:
primeira_linha = arquivo.readline()
segunda_linha = arquivo.readline()
print(f"1ª linha: {primeira_linha.strip()}")
print(f"2ª linha: {segunda_linha.strip()}")
# Navegando no arquivo
with open("exemplo.txt", "r", encoding="utf-8") as arquivo:
arquivo.seek(10) # Move o cursor para o 10º byte
trecho = arquivo.read(5)
print(f"5 caracteres a partir do 10º byte: {trecho}")
posicao_atual = arquivo.tell() # Retorna a posição atual
print(f"Posição atual: {posicao_atual}")
# Escrevendo texto simples
with open("saida.txt", "w", encoding="utf-8") as arquivo:
arquivo.write("Olá, mundo!\n")
arquivo.write("Esta é a segunda linha.\n")
# Escrevendo múltiplas linhas de uma vez
linhas = ["Primeira linha", "Segunda linha", "Terceira linha"]
with open("saida.txt", "w", encoding="utf-8") as arquivo:
arquivo.writelines(f"{linha}\n" for linha in linhas)
# Anexando ao final do arquivo
with open("saida.txt", "a", encoding="utf-8") as arquivo:
arquivo.write("Esta linha será anexada ao final.\n")
# Lendo, modificando e reescrevendo
with open("arquivo.txt", "r", encoding="utf-8") as arquivo:
conteudo = arquivo.read()
# Processa o conteúdo
conteudo_modificado = conteudo.replace("antigo", "novo")
with open("arquivo.txt", "w", encoding="utf-8") as arquivo:
arquivo.write(conteudo_modificado)
# Copiando conteúdo de um arquivo para outro
with open("origem.txt", "r", encoding="utf-8") as origem:
with open("destino.txt", "w", encoding="utf-8") as destino:
for linha in origem:
destino.write(linha)
Arquivos grandes
Para arquivos muito grandes, evite usar read()
para ler todo o conteúdo de uma vez, pois isso pode consumir muita memória. Prefira ler linha por linha ou em pequenos blocos, usando um loop.
Arquivos CSV (Comma-Separated Values)
CSV é um formato popular para armazenar dados tabulares (como planilhas) como texto.
import csv
# Lendo um CSV simples
with open("dados.csv", "r", encoding="utf-8", newline="") as arquivo:
leitor = csv.reader(arquivo)
for linha in leitor:
print(linha) # linha é uma lista de valores
# Lendo com cabeçalhos
with open("dados.csv", "r", encoding="utf-8", newline="") as arquivo:
leitor = csv.DictReader(arquivo) # Usa a primeira linha como chaves
for linha in leitor:
print(linha) # linha é um dicionário
print(f"Nome: {linha['nome']}, Idade: {linha['idade']}")
# Especificando delimitador
with open("dados.tsv", "r", encoding="utf-8", newline="") as arquivo:
leitor = csv.reader(arquivo, delimiter="\t") # Tabulação como delimitador
for linha in leitor:
print(linha)
import csv
# Escrevendo um CSV simples
dados = [
["Nome", "Idade", "Cidade"],
["João", "25", "São Paulo"],
["Maria", "30", "Rio de Janeiro"],
["Pedro", "22", "Belo Horizonte"]
]
with open("pessoas.csv", "w", encoding="utf-8", newline="") as arquivo:
escritor = csv.writer(arquivo)
for linha in dados:
escritor.writerow(linha) # Escreve uma linha
# Ou, mais diretamente:
with open("pessoas.csv", "w", encoding="utf-8", newline="") as arquivo:
escritor = csv.writer(arquivo)
escritor.writerows(dados) # Escreve todas as linhas
# Escrevendo com dicionários
dados_dict = [
{"nome": "João", "idade": 25, "cidade": "São Paulo"},
{"nome": "Maria", "idade": 30, "cidade": "Rio de Janeiro"},
{"nome": "Pedro", "idade": 22, "cidade": "Belo Horizonte"}
]
with open("pessoas_dict.csv", "w", encoding="utf-8", newline="") as arquivo:
campos = ["nome", "idade", "cidade"]
escritor = csv.DictWriter(arquivo, fieldnames=campos)
escritor.writeheader() # Escreve a linha de cabeçalho
escritor.writerows(dados_dict)
import csv
from collections import defaultdict
# Suponha um CSV com dados de vendas (produto, quantidade, valor)
# Analisar vendas por produto
vendas_por_produto = defaultdict(int)
total_vendas = 0
with open("vendas.csv", "r", encoding="utf-8", newline="") as arquivo:
leitor = csv.DictReader(arquivo)
for venda in leitor:
produto = venda["produto"]
quantidade = int(venda["quantidade"])
valor = float(venda["valor"])
vendas_por_produto[produto] += quantidade
total_vendas += quantidade * valor
# Exibir resultados
print(f"Total de vendas: R$ {total_vendas:.2f}")
print("\nQuantidade vendida por produto:")
for produto, quantidade in sorted(vendas_por_produto.items()):
print(f"{produto}: {quantidade} unidades")
Arquivos JSON
JSON (JavaScript Object Notation) é um formato leve para troca de dados, fácil de ler e escrever.
import json
# Lendo um arquivo JSON
with open("dados.json", "r", encoding="utf-8") as arquivo:
dados = json.load(arquivo)
print(type(dados)) # <class 'dict'> ou <class 'list'> dependendo do JSON
# Acessando dados
if isinstance(dados, dict):
print(f"Nome: {dados.get('nome')}")
print(f"Idade: {dados.get('idade')}")
elif isinstance(dados, list):
for item in dados:
print(f"Item: {item}")
import json
# Criando um dicionário
pessoa = {
"nome": "João Silva",
"idade": 30,
"cidade": "São Paulo",
"habilidades": ["Python", "JavaScript", "SQL"],
"ativo": True,
"contatos": {
"email": "[email protected]",
"telefone": "123-456-789"
}
}
# Salvando como JSON
with open("pessoa.json", "w", encoding="utf-8") as arquivo:
json.dump(pessoa, arquivo, indent=4, ensure_ascii=False)
# indent: formata o JSON para fácil leitura
# ensure_ascii=False: preserva caracteres não-ASCII (acentos, etc.)
# Convertendo para string JSON
json_str = json.dumps(pessoa, indent=4, ensure_ascii=False)
print(json_str)
# Salvando uma lista de objetos
pessoas = [
{"nome": "João", "idade": 30},
{"nome": "Maria", "idade": 25},
{"nome": "Pedro", "idade": 40}
]
with open("pessoas.json", "w", encoding="utf-8") as arquivo:
json.dump(pessoas, arquivo, indent=4, ensure_ascii=False)
import json
# Carregando um JSON complexo
with open("dados_complexos.json", "r", encoding="utf-8") as arquivo:
dados = json.load(arquivo)
# Navegando em uma estrutura aninhada
if "usuarios" in dados:
for usuario in dados["usuarios"]:
print(f"Nome: {usuario['nome']}")
if "enderecos" in usuario:
for endereco in usuario["enderecos"]:
print(f" Rua: {endereco['rua']}, Cidade: {endereco['cidade']}")
if "pedidos" in usuario:
total_pedidos = sum(pedido["valor"] for pedido in usuario["pedidos"])
print(f" Total de pedidos: R$ {total_pedidos:.2f}")
# Modificando e salvando
dados["ultima_atualizacao"] = "2023-05-10"
with open("dados_atualizados.json", "w", encoding="utf-8") as arquivo:
json.dump(dados, arquivo, indent=2, ensure_ascii=False)
import json
import csv
# JSON para CSV
with open("dados.json", "r", encoding="utf-8") as arquivo_json:
dados = json.load(arquivo_json)
# Supondo que dados é uma lista de dicionários
if dados and isinstance(dados, list):
# Extrai as chaves do primeiro item como cabeçalhos
campos = dados[0].keys()
with open("dados.csv", "w", encoding="utf-8", newline="") as arquivo_csv:
escritor = csv.DictWriter(arquivo_csv, fieldnames=campos)
escritor.writeheader()
escritor.writerows(dados)
# CSV para JSON
dados_json = []
with open("dados.csv", "r", encoding="utf-8", newline="") as arquivo_csv:
leitor = csv.DictReader(arquivo_csv)
for linha in leitor:
dados_json.append(linha)
with open("dados_convertidos.json", "w", encoding="utf-8") as arquivo_json:
json.dump(dados_json, arquivo_json, indent=4, ensure_ascii=False)
Trabalhando com Arquivos Binários
# Lendo arquivo binário
with open("imagem.jpg", "rb") as arquivo:
dados = arquivo.read()
print(f"Tamanho: {len(dados)} bytes")
print(f"Primeiros bytes: {dados[:10].hex()}")
# Escrevendo arquivo binário
dados_binarios = bytes([0x48, 0x65, 0x6C, 0x6C, 0x6F]) # "Hello" em ASCII
with open("dados.bin", "wb") as arquivo:
arquivo.write(dados_binarios)
def copiar_arquivo(origem, destino, tamanho_buffer=4096):
"""Copia um arquivo binário em blocos para economizar memória."""
with open(origem, "rb") as arquivo_origem:
with open(destino, "wb") as arquivo_destino:
while True:
buffer = arquivo_origem.read(tamanho_buffer)
if not buffer:
break
arquivo_destino.write(buffer)
# Uso
copiar_arquivo("imagem_original.jpg", "imagem_copia.jpg")
# Precisa instalar: pip install Pillow
from PIL import Image
# Abrir uma imagem
imagem = Image.open("foto.jpg")
print(f"Formato: {imagem.format}")
print(f"Tamanho: {imagem.size}")
print(f"Modo: {imagem.mode}")
# Redimensionar
imagem_redimensionada = imagem.resize((800, 600))
imagem_redimensionada.save("foto_redimensionada.jpg")
# Converter para escala de cinza
imagem_pb = imagem.convert("L")
imagem_pb.save("foto_pb.jpg")
# Recortar
area = (100, 100, 400, 400) # (left, upper, right, lower)
imagem_recortada = imagem.crop(area)
imagem_recortada.save("foto_recortada.jpg")
Gerenciamento de Caminhos e Diretórios
import os
import os.path
# Caminhos absolutos vs. relativos
caminho_absoluto = "/home/usuario/documentos/arquivo.txt"
caminho_relativo = "documentos/arquivo.txt"
# Obtendo o diretório atual
diretorio_atual = os.getcwd()
print(f"Diretório atual: {diretorio_atual}")
# Juntando caminhos (funciona em todos os sistemas operacionais)
caminho = os.path.join("pasta", "subpasta", "arquivo.txt")
print(caminho) # Ex: pasta/subpasta/arquivo.txt (no Unix)
# Extraindo componentes do caminho
arquivo = "/home/usuario/documentos/relatorio.pdf"
print(f"Diretório: {os.path.dirname(arquivo)}") # /home/usuario/documentos
print(f"Nome do arquivo: {os.path.basename(arquivo)}") # relatorio.pdf
print(f"Nome sem extensão: {os.path.splitext(os.path.basename(arquivo))[0]}") # relatorio
print(f"Extensão: {os.path.splitext(arquivo)[1]}") # .pdf
# Verificando existência
existe = os.path.exists(arquivo)
e_arquivo = os.path.isfile(arquivo)
e_diretorio = os.path.isdir(os.path.dirname(arquivo))
print(f"Existe? {existe}")
print(f"É arquivo? {e_arquivo}")
print(f"É diretório? {e_diretorio}")
# Obtendo informações do arquivo
if os.path.exists(arquivo):
tamanho = os.path.getsize(arquivo) # tamanho em bytes
data_mod = os.path.getmtime(arquivo) # timestamp da última modificação
import datetime
data_formatada = datetime.datetime.fromtimestamp(data_mod).strftime('%Y-%m-%d %H:%M:%S')
print(f"Tamanho: {tamanho} bytes")
print(f"Última modificação: {data_formatada}")
import os
import shutil
# Listar conteúdo de um diretório
for item in os.listdir("."): # "." representa o diretório atual
tipo = "Diretório" if os.path.isdir(item) else "Arquivo"
print(f"{item} - {tipo}")
# Listar com mais detalhes usando glob
import glob
# Todos os arquivos Python no diretório atual
arquivos_py = glob.glob("*.py")
print(f"Arquivos Python: {arquivos_py}")
# Todos os arquivos em uma estrutura de diretórios
arquivos_txt = glob.glob("**/*.txt", recursive=True)
print(f"Arquivos de texto em todos os subdiretórios: {arquivos_txt}")
# Criar diretório
os.makedirs("nova_pasta/subpasta", exist_ok=True)
# Copiar arquivo
shutil.copy2("origem.txt", "destino.txt")
# Mover arquivo
shutil.move("arquivo.txt", "nova_pasta/arquivo.txt")
# Remover arquivo
os.remove("arquivo_para_remover.txt")
# Remover diretório vazio
os.rmdir("diretorio_vazio")
# Remover diretório com conteúdo
shutil.rmtree("diretorio_com_arquivos")
import os
def listar_arquivos_recursivamente(diretorio):
"""Lista todos os arquivos em um diretório e seus subdiretórios."""
for raiz, dirs, arquivos in os.walk(diretorio):
print(f"\nDiretório: {raiz}")
if arquivos:
print("Arquivos:")
for arquivo in arquivos:
caminho_completo = os.path.join(raiz, arquivo)
tamanho = os.path.getsize(caminho_completo)
print(f" - {arquivo} ({tamanho} bytes)")
# Uso
listar_arquivos_recursivamente(".") # Lista a partir do diretório atual
Boas Práticas
# SEMPRE use 'with' para abrir arquivos
# Correto:
with open("arquivo.txt", "r") as arquivo:
conteudo = arquivo.read()
# Evite:
arquivo = open("arquivo.txt", "r")
try:
conteudo = arquivo.read()
finally:
arquivo.close()
# Ruim (pode deixar o arquivo aberto em caso de exceção):
arquivo = open("arquivo.txt", "r")
conteudo = arquivo.read()
arquivo.close()
# Sempre trate exceções ao trabalhar com arquivos
try:
with open("arquivo.txt", "r") as arquivo:
conteudo = arquivo.read()
except FileNotFoundError:
print("Arquivo não encontrado.")
except PermissionError:
print("Sem permissão para acessar o arquivo.")
except UnicodeDecodeError:
print("Erro de decodificação. Verifique o encoding do arquivo.")
except Exception as e:
print(f"Erro não esperado: {e}")
# Para operações em lote, continue mesmo após erro
arquivos = ["arquivo1.txt", "arquivo2.txt", "arquivo3.txt"]
for nome_arquivo in arquivos:
try:
with open(nome_arquivo, "r") as arquivo:
# Processa o arquivo
print(f"Processando {nome_arquivo}...")
except Exception as e:
print(f"Erro ao processar {nome_arquivo}: {e}")
continue # Continue para o próximo arquivo
# Otimizando leitura de grandes arquivos
# Em vez de ler todo o arquivo de uma vez:
def processar_arquivo_grande(nome_arquivo):
with open(nome_arquivo, "r") as arquivo:
# Processa uma linha por vez
for linha in arquivo:
# Faz algo com a linha
processar_linha(linha)
# Otimizando a escrita com buffers
def escrever_muitas_linhas(nome_arquivo, num_linhas):
with open(nome_arquivo, "w", buffering=8192) as arquivo:
for i in range(num_linhas):
arquivo.write(f"Linha {i}\n")
import os
# Sempre sanitize caminhos fornecidos por usuários
def caminho_seguro(base_dir, caminho_usuario):
"""Garante que o caminho do usuário não escape do diretório base."""
# Obter o caminho absoluto normalizado
caminho_completo = os.path.normpath(os.path.join(base_dir, caminho_usuario))
# Verificar se o caminho está dentro do diretório base
if não caminho_completo.startswith(os.path.abspath(base_dir)):
raise ValueError("Tentativa de acesso a um diretório não permitido")
return caminho_completo
# Uso
try:
arquivo = caminho_seguro("/home/usuario/arquivos", "../../../etc/passwd")
# Isso levantará uma exceção
except ValueError as e:
print(f"Erro: {e}")
Exemplos Práticos
# Analisador de arquivo de log simples
def analisar_log(arquivo_log):
erros = []
avisos = []
infos = 0
try:
with open(arquivo_log, "r", encoding="utf-8") as log:
for linha in log:
if "ERROR" in linha:
erros.append(linha.strip())
elif "WARNING" in linha:
avisos.append(linha.strip())
elif "INFO" in linha:
infos += 1
print(f"Análise do arquivo de log: {arquivo_log}")
print(f"Total de mensagens INFO: {infos}")
print(f"Total de avisos: {len(avisos)}")
print(f"Total de erros: {len(erros)}")
if erros:
print("\nErros encontrados:")
for erro em erros[:5]: # Primeiros 5 erros
print(f" {erro}")
if len(erros) > 5:
print(f" ... e mais {len(erros) - 5} erros")
except Exception as e:
print(f"Erro ao analisar o arquivo de log: {e}")
# Uso
analisar_log("aplicacao.log")
import json
import csv
import os
def converter_csv_para_json(arquivo_csv, arquivo_json=None):
"""Converte um arquivo CSV para JSON."""
if arquivo_json is None:
nome_base = os.path.splitext(arquivo_csv)[0]
arquivo_json = f"{nome_base}.json"
dados = []
try:
with open(arquivo_csv, "r", encoding="utf-8", newline="") as csv_file:
leitor = csv.DictReader(csv_file)
for linha em leitor:
# Converter strings numéricas para números
for chave, valor em linha.items():
if valor.isdigit():
linha[chave] = int(valor)
elif valor.replace(".", "", 1).isdigit() and valor.count(".") <= 1:
linha[chave] = float(valor)
dados.append(linha)
with open(arquivo_json, "w", encoding="utf-8") as json_file:
json.dump(dados, json_file, indent=2, ensure_ascii=False)
print(f"Arquivo convertido com sucesso: {arquivo_csv} -> {arquivo_json}")
print(f"Total de registros: {len(dados)}")
return True
except Exception as e:
print(f"Erro durante a conversão: {e}")
return False
# Uso
converter_csv_para_json("dados.csv")
import os
import shutil
import zipfile
import datetime
def fazer_backup(diretorio_origem, diretorio_destino=None):
"""Cria um backup compactado de um diretório."""
# Nome do arquivo de backup
data_atual = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
nome_diretorio = os.path.basename(os.path.normpath(diretorio_origem))
nome_backup = f"backup_{nome_diretorio}_{data_atual}.zip"
# Diretório de destino
if diretorio_destino is None:
diretorio_destino = os.getcwd()
caminho_backup = os.path.join(diretorio_destino, nome_backup)
try:
# Criar arquivo ZIP
with zipfile.ZipFile(caminho_backup, "w", zipfile.ZIP_DEFLATED) as zipf:
# Percorrer todos os arquivos e subdiretórios
for raiz, _, arquivos em os.walk(diretorio_origem):
for arquivo em arquivos:
caminho_completo = os.path.join(raiz, arquivo)
# Caminho relativo dentro do ZIP
caminho_relativo = os.path.relpath(caminho_completo, diretorio_origem)
zipf.write(caminho_completo, caminho_relativo)
print(f"Backup criado com sucesso: {caminho_backup}")
return caminho_backup
except Exception as e:
print(f"Erro ao criar backup: {e}")
return None
# Uso
fazer_backup("./meus_documentos", "./backups")
Resumo
Nesta aula, você aprendeu sobre:
- Operações básicas de leitura e escrita em arquivos
- Diferentes modos de abertura de arquivos
- Trabalhando com encodings para lidar com diferentes conjuntos de caracteres
- Manipulação de arquivos CSV para dados tabulares
- Manipulação de arquivos JSON para dados estruturados
- Trabalhando com arquivos binários
- Gerenciamento de caminhos e diretórios com os módulos
os
eos.path
- Boas práticas para trabalhar com arquivos em Python
- Exemplos práticos de manipulação de arquivos
Recursos de aprendizado
Próximos Passos
Na próxima aula, exploraremos iteradores e geradores, poderosas ferramentas para processamento de dados de forma eficiente.