Trabalhando com arquivos (os)
Esta aula aborda a manipulação de arquivos em Go utilizando o pacote os, incluindo abertura, leitura, escrita, definição de permissões e tratamento de erros. Você aprenderá a trabalhar com arquivos de forma prática, com exemplos de código e boas práticas.
Nesta aula, vamos explorar como trabalhar com arquivos em Go utilizando o pacote os. O pacote os fornece uma interface independente de plataforma para funcionalidades do sistema operacional, incluindo operações de arquivo. Dominar a leitura e escrita de arquivos é essencial para muitas aplicações, como processamento de dados, logs e configurações.
Além das operações básicas, veremos como definir permissões ao criar arquivos e como tratar erros de forma robusta. Ao final, você será capaz de manipular arquivos com confiança em seus programas Go.
Abrindo e lendo
Para abrir um arquivo em Go, usamos a função os.Open() que retorna um ponteiro para os.File e um erro. O arquivo é aberto no modo somente leitura. É importante sempre fechar o arquivo após o uso, geralmente com defer file.Close().
Existem várias formas de ler o conteúdo de um arquivo. Podemos ler todo o conteúdo de uma vez com os.ReadFile() (disponível desde Go 1.16), ou ler em partes usando um buffer e o método Read() do arquivo. Outra opção é usar um scanner (bufio.Scanner) para ler linha por linha.
Exemplo de leitura completa com os.ReadFile():
package main
import (
"fmt"
"os"
)
func main() {
data, err := os.ReadFile("exemplo.txt")
if err != nil {
fmt.Println("Erro ao ler arquivo:", err)
return
}
fmt.Println(string(data))
}Exemplo de leitura linha por linha com bufio.Scanner:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("exemplo.txt")
if err != nil {
fmt.Println("Erro ao abrir arquivo:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("Erro ao ler arquivo:", err)
}
}Escrevendo
Para escrever em um arquivo, podemos usar os.WriteFile() para escrever todo o conteúdo de uma vez, ou abrir o arquivo com os.Create() (que cria ou trunca o arquivo) e usar métodos como Write() ou WriteString(). Para adicionar conteúdo ao final de um arquivo existente, use os.OpenFile() com a flag os.O_APPEND.
Exemplo de escrita simples com os.WriteFile():
package main
import (
"fmt"
"os"
)
func main() {
conteudo := []byte("Olá, mundo!\n")
err := os.WriteFile("saida.txt", conteudo, 0644)
if err != nil {
fmt.Println("Erro ao escrever arquivo:", err)
return
}
fmt.Println("Arquivo escrito com sucesso")
}Exemplo de escrita com os.Create() e WriteString():
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Create("saida.txt")
if err != nil {
fmt.Println("Erro ao criar arquivo:", err)
return
}
defer file.Close()
_, err = file.WriteString("Escrevendo em Go\n")
if err != nil {
fmt.Println("Erro ao escrever:", err)
return
}
fmt.Println("Escrita bem-sucedida")
}Para adicionar ao final de um arquivo existente:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.OpenFile("saida.txt", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("Erro ao abrir arquivo:", err)
return
}
defer file.Close()
_, err = file.WriteString("Adicionando mais texto\n")
if err != nil {
fmt.Println("Erro ao escrever:", err)
return
}
}Permissões
Ao criar um arquivo com os.Create() ou os.OpenFile(), você pode especificar permissões no terceiro argumento. As permissões são representadas em octal, como 0644 (leitura/escrita para o dono, leitura para grupo e outros). O pacote os também fornece constantes como os.ModePerm (0777) e funções como os.Chmod() para alterar permissões após a criação.
Exemplo de criação com permissões específicas:
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.OpenFile("privado.txt", os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
fmt.Println("Erro:", err)
return
}
defer file.Close()
file.WriteString("Conteúdo privado")
}Alterando permissões de um arquivo existente:
package main
import (
"fmt"
"os"
)
func main() {
err := os.Chmod("privado.txt", 0644)
if err != nil {
fmt.Println("Erro ao alterar permissões:", err)
return
}
fmt.Println("Permissões alteradas")
}É importante lembrar que as permissões são aplicadas de acordo com a máscara de criação de arquivos (umask) do sistema. Em sistemas Unix, a permissão efetiva é a permissão solicitada menos a umask. Em Windows, as permissões são mapeadas de forma diferente.
Tratamento de erros
Todas as operações de arquivo em Go retornam um erro que deve ser verificado. Ignorar erros pode levar a comportamentos inesperados ou falhas silenciosas. É uma boa prática tratar os erros imediatamente, geralmente com uma estrutura if err != nil.
Além disso, podemos usar a função os.IsNotExist() para verificar se um erro específico indica que o arquivo não existe, e os.IsPermission() para verificar se é um erro de permissão.
Exemplo de tratamento robusto:
package main
import (
"fmt"
"os"
)
func main() {
data, err := os.ReadFile("inexistente.txt")
if err != nil {
if os.IsNotExist(err) {
fmt.Println("Arquivo não encontrado")
} else if os.IsPermission(err) {
fmt.Println("Permissão negada")
} else {
fmt.Println("Erro desconhecido:", err)
}
return
}
fmt.Println(string(data))
}Lembre-se de fechar arquivos com defer para garantir que o recurso seja liberado, mesmo em caso de erro. O defer é executado quando a função retorna, independentemente de como ela retorna.
Boas práticas
- Sempre feche arquivos usando
defer file.Close()imediatamente após abri-los. - Prefira
os.ReadFile()eos.WriteFile()para operações simples, pois eles lidam com abertura e fechamento automaticamente. - Use
bufio.Scannerpara ler arquivos grandes linha por linha, evitando carregar tudo na memória. - Verifique erros sempre e trate-os adequadamente, especialmente
os.IsNotExisteos.IsPermission. - Evite usar caminhos absolutos fixos; prefira caminhos relativos ou variáveis de ambiente.
Referências
- Documentação oficial do pacote os
- Documentação do pacote io
- Documentação do pacote bufio
- Usando módulos Go (blog oficial)
- How To Read and Write Files in Go (DigitalOcean)
Exercícios
Crie um programa que leia o conteúdo de um arquivo chamado "dados.txt" e exiba no terminal. Se o arquivo não existir, exiba uma mensagem de erro amigável.
✓ Resposta:package main import ( "fmt" "os" ) func main() { data, err := os.ReadFile("dados.txt") if err != nil { if os.IsNotExist(err) { fmt.Println("Arquivo dados.txt não encontrado") } else { fmt.Println("Erro ao ler arquivo:", err) } return } fmt.Println(string(data)) }Escreva um programa que crie um arquivo "saida.txt" e escreva nele os números de 1 a 10, um por linha.
✓ Resposta:package main import ( "fmt" "os" "strconv" ) func main() { file, err := os.Create("saida.txt") if err != nil { fmt.Println("Erro ao criar arquivo:", err) return } defer file.Close() for i := 1; i <= 10; i++ { _, err := file.WriteString(strconv.Itoa(i) + "\n") if err != nil { fmt.Println("Erro ao escrever:", err) return } } fmt.Println("Números escritos com sucesso") }Modifique o programa anterior para adicionar os números de 11 a 20 ao final do arquivo "saida.txt" (sem sobrescrever).
✓ Resposta:package main import ( "fmt" "os" "strconv" ) func main() { file, err := os.OpenFile("saida.txt", os.O_APPEND|os.O_WRONLY, 0644) if err != nil { fmt.Println("Erro ao abrir arquivo:", err) return } defer file.Close() for i := 11; i <= 20; i++ { _, err := file.WriteString(strconv.Itoa(i) + "\n") if err != nil { fmt.Println("Erro ao escrever:", err) return } } fmt.Println("Números adicionados com sucesso") }Crie um programa que leia um arquivo "config.txt" e exiba suas permissões atuais (em octal). Em seguida, altere as permissões para 0644.
✓ Resposta:package main import ( "fmt" "os" ) func main() { info, err := os.Stat("config.txt") if err != nil { fmt.Println("Erro ao obter informações:", err) return } fmt.Printf("Permissões atuais: %o\n", info.Mode().Perm()) err = os.Chmod("config.txt", 0644) if err != nil { fmt.Println("Erro ao alterar permissões:", err) return } fmt.Println("Permissões alteradas para 0644") }Escreva um programa que tente ler um arquivo "restrito.txt" e trate especificamente os erros de arquivo não encontrado e permissão negada.
✓ Resposta:package main import ( "fmt" "os" ) func main() { _, err := os.ReadFile("restrito.txt") if err != nil { if os.IsNotExist(err) { fmt.Println("Arquivo restrito.txt não existe") } else if os.IsPermission(err) { fmt.Println("Permissão negada para ler restrito.txt") } else { fmt.Println("Erro desconhecido:", err) } return } fmt.Println("Arquivo lido com sucesso") }