Por que trabalhar com arquivos em Go não é assustador, mas sim interessante?
\n\nQuando abri a documentação do pacote os em Go pela primeira vez, pensei que me esperava uma lista tediosa de chamadas de sistema. Mas, investigando mais a fundo, percebi: trabalhar com arquivos e o sistema de arquivos em Go não é apenas ler bytes. É uma ferramenta poderosa que lhe dá controle total sobre os dados, sejam eles arquivos de configuração, logs, binários ou bancos de dados inteiros em CSV.
Go foi criado para programação de sistemas, então seus autores (Ken Thompson, Rob Pike e outros) incorporaram o trabalho com arquivos diretamente no núcleo da linguagem. Sem dependências externas, apenas a biblioteca padrão, que funciona de forma rápida e previsível. Neste artigo, abordaremos todos os cenários: desde a simples leitura de um arquivo até a travessia recursiva de diretórios e o processamento em fluxo de grandes dados.
\n\nSe você programa em Go e quer trabalhar com o sistema de arquivos com confiança, está no lugar certo. Vamos lá!
\n\nFundamentos: abrindo, lendo, fechando
\n\nO primeiro passo é abrir o arquivo. Em Go, a função os.Open() é responsável por isso. Ela retorna um ponteiro para o arquivo e um erro. Erros em Go não são exceções, mas valores. Você deve sempre verificá-los.
package main\n\nimport (\n "fmt"\n "os"\n)\n\nfunc main() {\n file, err := os.Open("example.txt")\n if err != nil {\n fmt.Println("Erro ao abrir arquivo:", err)\n return\n }\n defer file.Close() // obrigatório fechar o arquivo\n\n // Lê o conteúdo\n buf := make([]byte, 1024)\n n, err := file.Read(buf)\n if err != nil {\n fmt.Println("Erro ao ler:", err)\n return\n }\n fmt.Printf("Lidos %d bytes: %s\\n", n, string(buf[:n]))\n}\n\n\nObserve o defer file.Close(). Esta é uma expressão idiomática do Go: a chamada adiada garante que o arquivo será fechado mesmo em caso de pânico ou retorno antecipado. Esquecer de fechar o arquivo é uma má prática que leva ao vazamento de descritores.
Lendo um arquivo inteiro em uma linha
\n\nSe o arquivo for pequeno (até alguns megabytes), você pode usar os.ReadFile() (substituiu o obsoleto ioutil.ReadFile). Isso é conveniente para configurações ou dados de texto.
data, err := os.ReadFile("config.yaml")\nif err != nil {\n log.Fatal(err)\n}\nfmt.Println(string(data))\n\n\nA função abre, lê e fecha o arquivo automaticamente. Mas lembre-se: para arquivos grandes, essa abordagem é perigosa — você carregará o arquivo inteiro na memória.
\n\nEscrevendo em arquivos: criando e anexando
\n\nPara escrever, use os.Create() (cria um novo arquivo ou trunca um existente) e os.OpenFile() para configurações mais refinadas (por exemplo, anexar ao final).
// Criando arquivo\nfile, err := os.Create("output.txt")\nif err != nil {\n panic(err)\n}\ndefer file.Close()\n\n// Escrevendo string\n_, err = file.WriteString("Olá, arquivos Go!\\n")\nif err != nil {\n panic(err)\n}\n\n// Escrevendo bytes\nbytes := []byte("Escrevendo slice de bytes\\n")\n_, err = file.Write(bytes)\nif err != nil {\n panic(err)\n}\n\n\nPara anexar dados ao final de um arquivo existente, use flags:
\n\nfile, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)\nif err != nil {\n log.Fatal(err)\n}\ndefer file.Close()\n\nfile.WriteString("Nova entrada no log\\n")\n\n\nA flag os.O_APPEND garante que os dados sejam anexados ao final, em vez de sobrescrever o início.
Trabalhando com diretórios: criando, excluindo, percorrendo
\n\nO sistema de arquivos não são apenas arquivos, mas também pastas. Go fornece funções convenientes para trabalhar com diretórios.
\n\nCriando e excluindo pastas
\n\n// Criar uma pasta\nerr := os.Mkdir("newdir", 0755)\nif err != nil {\n log.Fatal(err)\n}\n\n// Criar pastas aninhadas (como mkdir -p)\nerr = os.MkdirAll("path/to/nested/dir", 0755)\nif err != nil {\n log.Fatal(err)\