Trabalhando com arquivos e o pacote os em Golang
1. Introdução ao pacote os e operações básicas com arquivos
O pacote os da biblioteca padrão do Go fornece uma interface multiplataforma para operações do sistema operacional, incluindo manipulação de arquivos e diretórios. Vamos começar com as operações fundamentais.
Abrindo e fechando arquivos
package main
import (
"fmt"
"os"
)
func main() {
// Abrindo um arquivo existente (somente leitura)
arquivo, err := os.Open("exemplo.txt")
if err != nil {
fmt.Println("Erro ao abrir:", err)
return
}
defer arquivo.Close() // Fechamento garantido ao final da função
fmt.Println("Arquivo aberto com sucesso!")
}
Criando arquivos com os.Create
func criarArquivo() {
arquivo, err := os.Create("novo.txt")
if err != nil {
fmt.Println("Erro ao criar:", err)
return
}
defer arquivo.Close()
fmt.Println("Arquivo criado:", arquivo.Name())
}
Verificando existência e permissões com os.Stat
func verificarArquivo() {
info, err := os.Stat("exemplo.txt")
if os.IsNotExist(err) {
fmt.Println("Arquivo não existe")
return
}
if err != nil {
fmt.Println("Erro ao acessar:", err)
return
}
fmt.Printf("Nome: %s\nTamanho: %d bytes\nPermissões: %v\n",
info.Name(), info.Size(), info.Mode())
}
2. Leitura de arquivos
Leitura completa com os.ReadFile (Go 1.16+)
func lerArquivoCompleto() {
dados, err := os.ReadFile("exemplo.txt")
if err != nil {
fmt.Println("Erro na leitura:", err)
return
}
fmt.Println("Conteúdo:", string(dados))
}
Leitura em partes com os.File.Read
func lerEmPartes() {
arquivo, err := os.Open("exemplo.txt")
if err != nil {
fmt.Println(err)
return
}
defer arquivo.Close()
buffer := make([]byte, 100) // Lê de 100 em 100 bytes
for {
bytesLidos, err := arquivo.Read(buffer)
if err != nil {
break
}
fmt.Print(string(buffer[:bytesLidos]))
}
}
Leitura linha a linha com bufio.Scanner
import (
"bufio"
"os"
)
func lerLinhaPorLinha() {
arquivo, err := os.Open("exemplo.txt")
if err != nil {
fmt.Println(err)
return
}
defer arquivo.Close()
scanner := bufio.NewScanner(arquivo)
for scanner.Scan() {
linha := scanner.Text()
fmt.Println("Linha:", linha)
}
if err := scanner.Err(); err != nil {
fmt.Println("Erro no scanner:", err)
}
}
3. Escrita em arquivos
Escrita completa com os.WriteFile
func escreverCompleto() {
conteudo := []byte("Olá, mundo!\nSegunda linha")
err := os.WriteFile("saida.txt", conteudo, 0644)
if err != nil {
fmt.Println("Erro na escrita:", err)
return
}
}
Escrita incremental com Write e WriteString
func escreverIncremental() {
arquivo, err := os.Create("log.txt")
if err != nil {
fmt.Println(err)
return
}
defer arquivo.Close()
arquivo.WriteString("Primeira linha\n")
arquivo.Write([]byte("Segunda linha\n"))
arquivo.WriteString(fmt.Sprintf("Linha %d\n", 3))
}
Escrita formatada com fmt.Fprintf e bufio.Writer
import "bufio"
func escreverFormatado() {
arquivo, _ := os.Create("dados.txt")
defer arquivo.Close()
writer := bufio.NewWriter(arquivo)
defer writer.Flush() // Importante: descarrega o buffer
fmt.Fprintf(writer, "Nome: %s, Idade: %d\n", "João", 30)
fmt.Fprintf(writer, "Salário: R$ %.2f\n", 2500.50)
}
4. Manipulação de diretórios e caminhos
Listando conteúdo com os.ReadDir
func listarDiretorio() {
entradas, err := os.ReadDir(".")
if err != nil {
fmt.Println(err)
return
}
for _, entrada := range entradas {
tipo := "arquivo"
if entrada.IsDir() {
tipo = "diretório"
}
fmt.Printf("[%s] %s\n", tipo, entrada.Name())
}
}
Criando diretórios com os.Mkdir e os.MkdirAll
func criarDiretorios() {
err := os.Mkdir("novo_diretorio", 0755)
if err != nil {
fmt.Println("Erro ao criar diretório:", err)
}
// Cria diretórios aninhados
err = os.MkdirAll("a/b/c/d", 0755)
if err != nil {
fmt.Println("Erro:", err)
}
}
Navegação com os.Chdir e os.Getwd + path/filepath
import "path/filepath"
func navegarDiretorios() {
dirAtual, _ := os.Getwd()
fmt.Println("Diretório atual:", dirAtual)
caminho := filepath.Join(dirAtual, "subdir", "arquivo.txt")
fmt.Println("Caminho completo:", caminho)
dir := filepath.Dir(caminho)
base := filepath.Base(caminho)
fmt.Printf("Diretório: %s, Arquivo: %s\n", dir, base)
}
5. Operações avançadas: cópia, movimentação e exclusão
Copiando arquivos com io.Copy
import "io"
func copiarArquivo(origem, destino string) error {
src, err := os.Open(origem)
if err != nil {
return err
}
defer src.Close()
dst, err := os.Create(destino)
if err != nil {
return err
}
defer dst.Close()
_, err = io.Copy(dst, src)
return err
}
Renomeando e movendo com os.Rename
func moverArquivo() {
err := os.Rename("antigo.txt", "novo_local/antigo.txt")
if err != nil {
fmt.Println("Erro ao mover:", err)
return
}
fmt.Println("Arquivo movido com sucesso!")
}
Removendo arquivos e diretórios
func removerArquivos() {
// Remove um arquivo
err := os.Remove("temporario.txt")
if err != nil {
fmt.Println("Erro ao remover:", err)
}
// Remove diretório e todo seu conteúdo
err = os.RemoveAll("pasta_temporaria")
if err != nil {
fmt.Println("Erro:", err)
}
}
6. Metadados, permissões e links simbólicos
Obtendo informações detalhadas com os.FileInfo
func obterInfoDetalhada() {
info, err := os.Stat("exemplo.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Tamanho: %d bytes\n", info.Size())
fmt.Printf("Modo: %v\n", info.Mode())
fmt.Printf("Modificação: %v\n", info.ModTime())
fmt.Printf("É diretório: %t\n", info.IsDir())
}
Alterando permissões com os.Chmod e proprietário com os.Chown
func alterarPermissoes() {
// Torna o arquivo executável
err := os.Chmod("script.sh", 0755)
if err != nil {
fmt.Println("Erro ao alterar permissões:", err)
}
// Altera proprietário (UID e GID)
err = os.Chown("exemplo.txt", 1000, 1000)
if err != nil {
fmt.Println("Erro ao alterar proprietário:", err)
}
}
Trabalhando com links simbólicos
func gerenciarLinks() {
// Criar link simbólico
err := os.Symlink("original.txt", "link_para_original")
if err != nil {
fmt.Println("Erro ao criar link:", err)
}
// Ler o destino do link
destino, err := os.Readlink("link_para_original")
if err != nil {
fmt.Println("Erro ao ler link:", err)
}
fmt.Println("Link aponta para:", destino)
// Lstat para obter info do link (não do destino)
info, _ := os.Lstat("link_para_original")
fmt.Println("É link simbólico:", info.Mode()&os.ModeSymlink != 0)
}
7. Boas práticas e tratamento de erros
Uso de defer para fechamento seguro
func processarArquivoSeguro() error {
arquivo, err := os.Open("dados.txt")
if err != nil {
return err
}
defer arquivo.Close() // Garantido mesmo em caso de panic
// ... processamento ...
return nil
}
Lidando com erros comuns
func tratarErrosArquivo() {
_, err := os.Open("inexistente.txt")
if os.IsNotExist(err) {
fmt.Println("Arquivo não encontrado")
} else if os.IsPermission(err) {
fmt.Println("Permissão negada")
} else if err != nil {
fmt.Println("Erro desconhecido:", err)
}
}
Padrões para leitura/escrita concorrente
import "sync"
type ArquivoSeguro struct {
sync.Mutex
arquivo *os.File
}
func (a *ArquivoSeguro) Escrever(dados []byte) (int, error) {
a.Lock()
defer a.Unlock()
return a.arquivo.Write(dados)
}
// Exemplo com canais
func processarConcorrente(dados <-chan []byte, arquivo *os.File) {
var wg sync.WaitGroup
for chunk := range dados {
wg.Add(1)
go func(chunk []byte) {
defer wg.Done()
arquivo.Write(chunk)
}(chunk)
}
wg.Wait()
}
Referências
- Documentação oficial do pacote os — Referência completa de todas as funções e tipos do pacote os
- Working with Files in Go - DigitalOcean — Tutorial prático sobre manipulação de arquivos em Go
- Go by Example: Reading Files — Exemplos práticos de leitura de arquivos em Go
- Go File I/O Cheat Sheet — Referência rápida com exemplos de operações de entrada e saída
- Effective Go - The Go Programming Language — Guia oficial com boas práticas de programação em Go, incluindo tratamento de arquivos
- Path/filepath Package Documentation — Documentação oficial para manipulação de caminhos de arquivos
- bufio Package Documentation — Documentação do pacote bufio para leitura e escrita com buffer