Pular para o conteúdo

Arquivos

Ler, escrever e organizar arquivos com Arcane.IO, e compactar com Arcane.Archive.

Texto#

dataforge
adopt Arcane.IO as IO

IO.write(caminho, texto)      # cria ou substitui
IO.append(caminho, texto)     # acrescenta ao fim
IO.read(caminho)              # devolve o conteúdo

cycle linha in IO.read("dados.txt").lines():
    processar(linha)

Formatos estruturados#

dataforge
IO.write_json(caminho, vault)     IO.read_json(caminho)
IO.write_csv(caminho, linhas)     IO.read_csv(caminho)

Fazem a serialização e a escrita numa chamada só.

Caminhos#

dataforge
IO.join(pasta, "arquivo.txt")     # monta com o separador certo
IO.basename(caminho)              # "notas.txt"
IO.dirname(caminho)               # a pasta
IO.ext(caminho)                   # ".txt"
IO.abs(caminho)                   # caminho absoluto

Diretórios#

dataforge
IO.mkdir(pasta)          # cria, inclusive os níveis intermediários
IO.list_dir(pasta)       # os nomes dentro dela
IO.exists(caminho)       # arquivo ou pasta
IO.file_exists(caminho)  # só arquivo
IO.size(caminho)         # bytes

Copiar, mover, apagar#

dataforge
IO.copy(origem, destino)
IO.rename(antigo, novo)     # também move entre pastas
IO.delete(caminho)

Filtrar por extensão#

dataforge
textos := IO.list_dir(pasta) >> sift nome: nome.endswith(".txt")

list_dir devolve uma lista comum, então todo o pipeline funciona sobre ela.

Zip e tar#

Arcane.Archive compacta e extrai. É um módulo à parte porque a extração tem uma responsabilidade que a leitura de arquivo não tem: o conteúdo veio de fora.

dataforge
adopt Arcane.Archive as Zip

Zip.compactar("relatorios", "backup.zip")     # pasta inteira
Zip.extrair("backup.zip", "destino")
Zip.listar("backup.zip")                      # sem extrair
Zip.ler_de("backup.zip", "relatorios/jan.csv")   # um arquivo só
Zip.conferir("backup.zip")                    # está íntegro?
Zip.acrescentar("backup.zip", "mar.csv")

Zip.compactar_tar("projeto", "projeto.tar.gz")
Zip.extrair_tar("projeto.tar.gz", "destino")

O que a extração recusa#

Extrair um arquivo de origem desconhecida é perigoso, e os dois riscos têm nome próprio justamente por terem sido explorados muitas vezes:

AtaqueO que fazA defesa aqui
Zip Slipuma entrada chamada ../../.ssh/authorized_keys escreve fora da pasta de destinoo caminho final é resolvido e comparado com o destino; .. e link simbólico já foram desfeitos
Zip bomb1 KB que descompacta para 10 GB, enchendo o discoo tamanho declarado é somado antes de extrair
Link no taruma entrada que é link simbólico e aponta para qualquer lugar do sistemarecusada; e o filter="data" do Python barra permissão estranha e dono de outro usuário

Para guardar o pacote cifrado, Arcane.Crypto.cifrar_pasta compacta e cifra num passo — ver Criptografia.

Limpeza garantida#

Use defer para garantir a limpeza mesmo se algo falhar no meio:

dataforge
action processar():
    IO.write(temp, "dados")
    defer:
        given IO.exists(temp):
            IO.delete(temp)
    # ... o defer roda mesmo se isto disparar
    yield resultado

Detalhes em Tratamento de erros.

Um relatório de pasta#

dataforge
action formatar_bytes(n) -> String:
    given n smaller 1024:
        yield $"{n} B"
    orif n smaller 1048576:
        yield $"{round(n / 1024, 1)} KB"
    yield $"{round(n / 1048576, 2)} MB"

cycle nome in IO.list_dir("."):
    caminho := IO.join(".", nome)
    given IO.file_exists(caminho):
        out $"{nome.pad_end(24)}{formatar_bytes(IO.size(caminho)).pad_start(10)}"