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)
    }
}

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