Pular para o conteúdo

Sistema de arquivos

Ler, escrever, percorrer e não deixar lixo — Arcane.IO e Arcane.OS na prática.

Dois módulos dividem o trabalho: `Arcane.IO` mexe em arquivos e pastas; `Arcane.OS` responde sobre o ambiente — onde é a casa, qual é o temporário, o que há no PATH.

Ler e escrever#

dataforge
adopt Arcane.IO as IO

IO.write("notas.txt", "primeira linha\n")
IO.append("notas.txt", "segunda linha\n")

out IO.read("notas.txt").strip().split("\n")
out IO.exists("notas.txt"), IO.size("notas.txt")
saída
[primeira linha, segunda linha]
yes 29

IO.write sobrescreve; IO.append acrescenta. Não há modo intermediário de propósito: um terceiro verbo com semântica sutil é o tipo de coisa que se erra às três da manhã.

Caminhos: monte, não concatene#

dataforge
IO.join(pasta, "dados", "brutos.csv")    // usa o separador do sistema
IO.basename("/tmp/a/b.txt")              // b.txt
IO.dirname("/tmp/a/b.txt")               // /tmp/a
IO.ext("/tmp/a/b.txt")                   // .txt
IO.abs("./relativo")                     // o caminho absoluto

Concatenar com + produz um caminho que funciona no seu computador e falha no Windows — e o teste local nunca pega.

JSON e CSV, sem cerimônia#

dataforge
adopt Arcane.IO as IO

IO.write_json("config.json", {"porta": 8000, "debug": yes})
out IO.read_json("config.json")["porta"]          // 8000

// um cluster de VAULTS: a primeira linha vira o cabeçalho
IO.write_csv("dados.csv", [{"nome": "Ana", "idade": 30},
                           {"nome": "Bruno", "idade": 25}])
out IO.read("dados.csv").strip()
saída
nome,idade
Ana,30
Bruno,25

Na leitura, escolha a forma:

dataforge
linhas := IO.read_csv("dados.csv")          // cluster de clusters
linhas := IO.read_csv("dados.csv", yes)     // cluster de VAULTS, pelo cabeçalho

Pastas#

dataforge
adopt Arcane.IO as IO

IO.mkdir("saida")                    // cria, inclusive os pais
out IO.list_dir("saida")             // o que há dentro
IO.copy("a.txt", "saida/a.txt")
IO.copy_tree("modelos", "saida/modelos")
IO.rename("saida/a.txt", "saida/b.txt")
IO.delete("saida/b.txt")             // um arquivo
IO.remove_tree("saida")              // a pasta inteira

Temporário: sempre uma subpasta sua#

dataforge
adopt Arcane.IO as IO
adopt Arcane.OS as OS

base := $"{OS.temp_dir()}/meu-programa-{randint(100000, 999999)}"
IO.mkdir(base)

defer:
    IO.remove_tree(base)         // roda na saída da ação, inclusive por erro

IO.write(IO.join(base, "trabalho.txt"), "…")

O defer é o que garante a limpeza quando a ação sai por erro — que é justamente quando ninguém lembra de limpar.

O ambiente, com `Arcane.OS`#

PerguntaResposta
onde estou?OS.cwd()
onde está o script?OS.script_dir()
qual é a casa do usuário?OS.home()
qual é o temporário?OS.temp_dir()
qual sistema?OS.is_windows(), OS.is_mac(), OS.is_linux()
quantos núcleos?OS.cpu_count()
a variável de ambienteOS.get_env(nome, padrao), OS.set_env, OS.unset_env
este comando existe?OS.which("git")

OS.unset_env existe porque a falta dela aparecia como poluição entre execuções: um exercício imprimia 77 variáveis na primeira rodada e 78 na segunda. Ela devolve yes/no em vez de levantar — remover é pedir um estado final, e nesse ponto já não importa se estava lá.

Arquivo grande: não carregue inteiro#

IO.read traz tudo para a memória. Para um arquivo maior que a RAM, processe por partes — ver complexidade de espaço.

dataforge
// O(n) de espaço: o arquivo inteiro na memória
todas := IO.read("grande.csv").split("\n")

// O(1) de espaço: uma linha por vez
stream action linhas_de(caminho):
    cycle linha in IO.read(caminho).split("\n"):
        emit linha

out linhas_de("grande.csv").take(3)

Erros: o que pode falhar, e como#

SituaçãoO que acontece
ler arquivo que não existeerro, com o caminho na mensagem
escrever em pasta que não existeerro — crie com IO.mkdir antes
escrever sem permissãoerro do sistema, traduzido
delete de algo ausenteerro — confira com IO.exists
arquivo fora de UTF-8erro de leitura, dizendo qual arquivo
dataforge
monitor:
    config := IO.read_json("config.json")
handle Error as e:
    out $"usando o padrao: {e.message}"
    config := {"porta": 8000}

Por onde seguir#