Operações com Arquivos em Python

Operações com Arquivos em Python

Este artigo explica as operações com arquivos no Python.

Com foco em "segurança", "eficiência" e "legibilidade", este guia explica conceitos passo a passo, desde o básico até técnicas práticas.

YouTube Video

Operações com Arquivos em Python

Operações com arquivos são uma habilidade fundamental essencial, desde pequenos scripts até aplicações em larga escala.

Abrindo e fechando arquivos

Primeiramente, vejamos exemplos de leitura e escrita de arquivos de texto. Usar a instrução with (gerenciador de contexto) garante que o arquivo seja fechado corretamente.

O código a seguir abre um arquivo de texto, lê seu conteúdo e o processa linha por linha.

1# Open and read a text file safely using a context manager.
2with open("example.txt", "r", encoding="utf-8") as f:
3    # Read all lines lazily (as an iterator) and process each line.
4    for line in f:
5        print(line.rstrip())
  • Especificar explicitamente encoding="utf-8" ajuda a reduzir problemas dependentes da plataforma.
  • Mesmo com arquivos grandes, for line in f é eficiente em termos de memória.

Escrita (sobrescrever e adicionar)

Preste atenção ao modo ao escrever em arquivos. w é para sobrescrever, a é para acrescentar. Use também a instrução with ao escrever.

O código a seguir mostra exemplos básicos de sobrescrita e acréscimo.

1# Write (overwrite) to a file.
2with open("output.txt", "w", encoding="utf-8") as f:
3    f.write("First line\n")
4    f.write("Second line\n")
5
6# Append to the same file.
7with open("output.txt", "a", encoding="utf-8") as f:
8    f.write("Appended line\n")
  • Este código escreve texto em output.txt e depois acrescenta mais texto ao mesmo arquivo. O modo "w" sobrescreve o arquivo, enquanto o modo "a" acrescenta uma nova linha ao final do conteúdo existente.
  • Se precisar forçar a gravação, chame flush(), mas normalmente o conteúdo é gravado automaticamente quando o contexto termina.
  • Se vários processos ou threads puderem escrever ao mesmo tempo, é necessário considerar o controle exclusivo, como o bloqueio de arquivos.

Leitura e escrita de dados binários

Imagens e arquivos compactados são manipulados em modo binário (rb ou wb). Diferente do modo texto, encoding é ignorado.

O código a seguir lê um arquivo binário e o copia para outro arquivo em modo binário.

1# Read and write binary files.
2with open("image.png", "rb") as src:
3    data = src.read()
4
5with open("copy.png", "wb") as dst:
6    dst.write(data)
  • Ao lidar com arquivos binários grandes, evite ler tudo de uma vez com read(); ler e escrever em blocos é mais eficiente em memória.

Exemplo de manipulação de arquivos grandes em blocos

Para arquivos gigantes que não cabem na memória, leia e escreva em blocos de tamanho fixo. Como depende de I/O, ajustar o tamanho do buffer é útil.

O código a seguir copia um arquivo em blocos de 64KB. Isto opera rapidamente e mantém o uso de memória baixo.

1# Copy a large file in chunks to avoid using too much memory.
2CHUNK_SIZE = 64 * 1024  # 64 KB
3
4with open("large_source.bin", "rb") as src,      open("large_dest.bin", "wb") as dst:
5    while True:
6        chunk = src.read(CHUNK_SIZE)
7        if not chunk:
8            break
9        dst.write(chunk)
  • Você pode ajustar o tamanho do bloco conforme as características da sua rede ou disco. Em SSDs, um tamanho de bloco um pouco maior costuma funcionar melhor.

Exemplo usando o argumento buffering

Ao especificar o argumento buffering na função open(), você pode controlar o tamanho do buffer usado internamente pelo Python. Isso permite otimizar ainda mais a eficiência de entrada/saída.

 1# Explicitly set the internal buffer size to 128 KB for faster I/O.
 2CHUNK_SIZE = 64 * 1024
 3BUFFER_SIZE = 128 * 1024  # 128 KB internal buffer
 4
 5with open("large_source.bin", "rb", buffering=BUFFER_SIZE) as src,      open("large_dest.bin", "wb", buffering=BUFFER_SIZE) as dst:
 6    while True:
 7        chunk = src.read(CHUNK_SIZE)
 8        if not chunk:
 9            break
10        dst.write(chunk)
  • Se você definir o valor de buffering como 0, as operações de E/S são realizadas sem buffer; 1 ativa o buffer por linha, enquanto valores de 2 ou maiores usam um buffer do tamanho especificado em bytes.
  • Em geral, o valor padrão é suficiente porque o sistema operacional gerencia eficientemente o cache, mas ajustar este parâmetro pode ser eficaz para arquivos muito grandes ou dispositivos especiais.

Operações modernas de arquivos usando Pathlib

O módulo padrão pathlib torna o manuseio de caminhos mais intuitivo. Isso melhora a legibilidade e a segurança em comparação com caminhos em formato de string.

 1from pathlib import Path
 2
 3path = Path("data") / "notes.txt"
 4
 5# Ensure parent directory exists.
 6path.parent.mkdir(parents=True, exist_ok=True)
 7
 8# Write and read using pathlib.
 9path.write_text("Example content\n", encoding="utf-8")
10content = path.read_text(encoding="utf-8")
11print(content)
  • Este código demonstra como criar um diretório com pathlib, escrever em um arquivo de texto e ler seu conteúdo. Com um objeto Path, você pode manipular caminhos de forma intuitiva e segura.
  • Path possui APIs convenientes como iterdir() e glob(). Você pode escrever código sem se preocupar com separadores de caminho entre diferentes sistemas operacionais.

Arquivos e diretórios temporários (tempfile)

Arquivos temporários podem ser criados com segurança usando tempfile. Isto evita condições de corrida de segurança e colisões de nomes.

O código a seguir mostra um exemplo de criação de dados temporários usando um arquivo temporário.

 1import tempfile
 2
 3# Create a temporary file that is deleted on close.
 4with tempfile.NamedTemporaryFile(
 5    mode="w+",
 6    encoding="utf-8",
 7    delete=True
 8) as tmp:
 9    tmp.write("temporary data\n")
10    tmp.seek(0)
11    print(tmp.read())
12
13# tmp is deleted here
  • Este código cria um arquivo temporário, escreve e lê dados, e o exclui automaticamente ao fim do bloco with. Utilizando tempfile.NamedTemporaryFile, você pode manipular arquivos temporários com segurança e sem conflitos. Como delete=True está especificado, o arquivo é excluído automaticamente.
  • No Windows, pode não ser possível abrir o arquivo de outro manipulador imediatamente, então você pode definir delete=False e gerenciar a exclusão manualmente.

shutil: Operações de alto nível para copiar, mover e excluir

A cópia, movimentação e exclusão recursiva de arquivos e diretórios é fácil com o shutil.

 1import shutil
 2
 3# Copy a file preserving metadata.
 4shutil.copy2("source.txt", "dest.txt")
 5
 6# Move a file or directory.
 7shutil.move("old_location", "new_location")
 8
 9# Remove an entire directory tree (use with caution).
10shutil.rmtree("some_directory")
  • shutil.copy2 também copia metadados (como data de modificação). move fará a movimentação do arquivo mesmo se a renomeação não puder ser usada.
  • rmtree é uma operação perigosa, então sempre confirme e faça backup antes de excluir.

Metadados de arquivos (os.stat) e manipulação de permissões

O tamanho do arquivo, a data de modificação e as permissões podem ser lidos e modificados com os e stat.

O código a seguir obtém informações básicas do arquivo com os.stat e altera permissões com os.chmod.

 1import os
 2import stat
 3from datetime import datetime
 4
 5st = os.stat("example.txt")
 6print("size:", st.st_size)
 7print("modified:", datetime.fromtimestamp(st.st_mtime))
 8
 9# Make file read-only for owner.
10os.chmod("example.txt", stat.S_IREAD)
  • As permissões se comportam de forma diferente em sistemas POSIX e Windows. Se a compatibilidade entre plataformas for importante, utilize APIs de alto nível ou adicione manipulação condicional.

Bloqueio de arquivos (controle exclusivo) — Diferenças entre Unix e Windows

É necessário controle exclusivo quando vários processos acessam o mesmo arquivo em paralelo. UNIX utiliza fcntl e Windows utiliza msvcrt.

O código a seguir usa fcntl.flock em sistemas UNIX para adquirir um bloqueio exclusivo ao gravar.

 1# Unix-style file locking example
 2import fcntl
 3
 4with open("output.txt", "a+", encoding="utf-8") as f:
 5    fcntl.flock(f, fcntl.LOCK_EX)
 6    try:
 7        f.write("Safe write\n")
 8        f.flush()
 9    finally:
10        fcntl.flock(f, fcntl.LOCK_UN)
  • Este código adquire um bloqueio exclusivo usando fcntl.flock em sistemas UNIX-like para gravar com segurança e evitar escritas simultâneas no arquivo. Sempre libere o bloqueio após o processamento para permitir o acesso de outros processos.
  • No Windows, utilize msvcrt.locking(). Para uso de alto nível, considere bibliotecas externas como portalocker ou filelock.

Padrões de escrita atômica de arquivos

Para evitar a corrupção do arquivo durante atualizações, escreva em um arquivo temporário e substitua usando os.replace em caso de sucesso.

Escrever em um arquivo temporário e depois substituir evita corrupção se ocorrer uma falha.

 1import os
 2from pathlib import Path
 3import tempfile
 4
 5def atomic_write(path: Path, data: str, encoding="utf-8"):
 6    # Write to a temp file in the same directory and atomically replace.
 7    dirpath = path.parent
 8    with tempfile.NamedTemporaryFile(
 9        mode="w",
10        encoding=encoding,
11        dir=str(dirpath),
12        delete=False
13    ) as tmp:
14        tmp.write(data)
15        tmp.flush()
16        tempname = tmp.name
17
18    # Atomic replacement (on most OSes)
19    os.replace(tempname, str(path))
20
21# Usage
22atomic_write(Path("config.json"), '{"key":"value"}\n')

os.replace faz uma substituição atômica dentro do mesmo sistema de arquivos. Note que a atomicidade não é garantida entre diferentes pontos de montagem.

Acesso rápido usando mmap (para dados em grande escala)

Para acesso aleatório a arquivos grandes, mmap melhora o desempenho de I/O. Envolve principalmente operações binárias.

O código a seguir faz o mapeamento de um arquivo na memória e lê ou grava intervalos específicos de bytes. Cuidado ao alterar o tamanho do arquivo.

1import mmap
2
3with open("data.bin", "r+b") as f:
4    mm = mmap.mmap(f.fileno(), 0)  # map entire file
5    try:
6        print(mm[:20])  # first 20 bytes
7        mm[0:4] = b"\x00\x01\x02\x03"  # modify bytes
8    finally:
9        mm.close()
  • Este código mapeia um arquivo binário na memória com mmap e executa operações de leitura/escrita em nível de byte. O mapeamento de memória possibilita acesso aleatório rápido a grandes conjuntos de dados.
  • mmap é eficiente, mas o uso incorreto pode levar a problemas de consistência de dados. Chame flush() para sincronizar, se necessário.

CSV / JSON / Pickle: Leitura e escrita por formato

Formatos de dados específicos possuem módulos dedicados. Use csv para CSV, json para JSON e pickle para salvar objetos Python.

O código a seguir mostra exemplos básicos de leitura e escrita de CSV e JSON, e uso do Pickle. Como o Pickle pode executar código arbitrário, evite carregar dados de fontes não confiáveis.

 1import csv
 2import json
 3import pickle
 4
 5# CSV write
 6with open("rows.csv", "w", encoding="utf-8", newline="") as f:
 7    writer = csv.writer(f)
 8    writer.writerow(["name", "age"])
 9    writer.writerow(["Alice", 30])
10
11# JSON write
12data = {"items": [1, 2, 3]}
13with open("data.json", "w", encoding="utf-8") as f:
14    json.dump(data, f, ensure_ascii=False, indent=2)
15
16# Pickle write (only for trusted environments)
17obj = {"key": "value"}
18with open("obj.pkl", "wb") as f:
19    pickle.dump(obj, f)
  • É recomendável especificar newline="" para CSV para evitar linhas em branco extras no Windows. Com ensure_ascii=False, o JSON mantém os caracteres UTF-8 legíveis.

Leitura e escrita direta de arquivos compactados (gzip / bz2 / zipfile)

Manipular gzip e zip diretamente pode economizar espaço em disco. A biblioteca padrão inclui os módulos correspondentes.

O código a seguir é um exemplo simples de leitura e escrita de arquivos gzip como texto.

1import gzip
2
3# Write gzipped text.
4with gzip.open("text.gz", "wt", encoding="utf-8") as f:
5    f.write("Compressed content\n")
6
7# Read gzipped text.
8with gzip.open("text.gz", "rt", encoding="utf-8") as f:
9    print(f.read())
  • Existe uma relação de compromisso entre a taxa de compressão e a velocidade, dependendo do nível de compressão e do formato.

Medidas de segurança e vulnerabilidade

Os seguintes pontos podem ser considerados para medidas de segurança e vulnerabilidade.

  • Não utilize entradas não confiáveis diretamente em nomes ou caminhos de arquivos.
  • Use Pickle apenas com fontes confiáveis.
  • Minimize os privilégios de execução e conceda apenas as permissões mínimas aos processos que manipulam arquivos.
  • Utilize arquivos temporários com tempfile e não armazene arquivos simples em diretórios públicos.

Se utilizar entrada do usuário em caminhos de arquivos, são necessárias normalização e validação. Por exemplo, utilize Path.resolve() e verifique os diretórios pai.

 1from pathlib import Path
 2
 3def safe_path(base_dir: Path, user_path: str) -> Path:
 4    candidate = (base_dir / user_path).resolve()
 5    if base_dir.resolve() not in candidate.parents and base_dir.resolve() != candidate:
 6        raise ValueError("Invalid path")
 7    return candidate
 8
 9# Usage
10base = Path("/srv/app/data")
11
12# Raises an exception: attempted path traversal outside `base`
13safe = safe_path(base, '../etc/passwd')
  • Tenha cuidado especial ao usar entrada externa como caminhos de arquivos em aplicativos web ou APIs públicas.

Resumo de padrões comuns

  • Sempre utilize a instrução with (fechamento automático).
  • Especifique explicitamente o encoding para dados de texto.
  • Leia e escreva arquivos grandes em blocos.
  • Implemente bloqueio de arquivos para recursos compartilhados.
  • Para atualizações críticas, utilize o padrão atômico de 'escrever em arquivo temporário → os.replace'.
  • Sempre confirme e crie backups antes de realizar operações perigosas (como exclusão ou sobrescrita).
  • Normalize e valide ao utilizar entradas externas como caminhos de arquivos.

Resumo

Para operações de arquivo, é importante usar técnicas seguras e confiáveis, como o uso da instrução with, especificação explícita de codificação e escritas atômicas. Para processamento em grande escala ou acesso paralelo, é necessário implementar sistemas de bloqueio e gerenciamento de logs para evitar corrupção e conflitos de dados. O equilíbrio entre eficiência e segurança é a chave para operações de arquivo confiáveis.

Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.

YouTube Video