← Volver al listado de tecnologías
I/O, OS y Manejo de Archivos
I/O, OS y Manejo de Archivos
Go modela toda entrada/salida alrededor de dos interfaces fundamentales: io.Reader e io.Writer. Esta abstraccion permite que archivos, conexiones de red, buffers y HTTP compartan la misma API.
io.Reader e io.Writer
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
Cualquier tipo que implemente estos metodos puede usarse donde se espere un Reader o Writer.
| Implementa Reader | Implementa Writer | Ambos |
|---|---|---|
os.File | os.File | os.File |
strings.Reader | bytes.Buffer | bytes.Buffer |
http.Request.Body | http.ResponseWriter | net.Conn |
bufio.Reader | bufio.Writer | os.Stdout |
Leer archivos
os.ReadFile (Go 1.16+)
La forma mas simple de leer un archivo completo en memoria.
package main
import (
"fmt"
"os"
)
func main() {
datos, err := os.ReadFile("config.txt")
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
fmt.Println(string(datos))
}
os.Open con io.ReadAll
Para cuando necesitas el *os.File (por ejemplo para obtener metadata).
package main
import (
"fmt"
"io"
"os"
)
func main() {
archivo, err := os.Open("datos.txt") // Solo lectura
if err != nil {
fmt.Println("Error:", err)
return
}
defer archivo.Close() // Siempre cerrar con defer
contenido, err := io.ReadAll(archivo)
if err != nil {
fmt.Println("Error leyendo:", err)
return
}
fmt.Println(string(contenido))
}
Leer linea por linea con bufio.Scanner
Ideal para archivos grandes donde no quieres cargar todo en memoria.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
archivo, err := os.Open("log.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer archivo.Close()
scanner := bufio.NewScanner(archivo)
linea := 0
for scanner.Scan() {
linea++
fmt.Printf("%d: %s\n", linea, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("Error de lectura:", err)
}
}
bufio.Reader para lectura con buffer
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
archivo, err := os.Open("datos.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer archivo.Close()
reader := bufio.NewReader(archivo)
// Leer hasta un delimitador
linea, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Print("Primera linea:", linea)
}
Escribir archivos
os.WriteFile (Go 1.16+)
package main
import "os"
func main() {
contenido := []byte("Hola desde Go\nSegunda linea\n")
// 0644 = owner lee/escribe, grupo y otros solo leen
err := os.WriteFile("salida.txt", contenido, 0644)
if err != nil {
panic(err)
}
}
os.Create para escritura manual
package main
import (
"fmt"
"os"
)
func main() {
archivo, err := os.Create("reporte.txt") // Crea o trunca
if err != nil {
fmt.Println("Error:", err)
return
}
defer archivo.Close()
fmt.Fprintln(archivo, "Reporte de ventas")
fmt.Fprintf(archivo, "Total: $%d\n", 15000)
archivo.WriteString("Fin del reporte\n")
}
Abrir con flags especificos
package main
import (
"fmt"
"os"
)
func main() {
// Abrir para append, crear si no existe
archivo, err := os.OpenFile("log.txt",
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("Error:", err)
return
}
defer archivo.Close()
fmt.Fprintln(archivo, "Nueva entrada de log")
}
| Flag | Descripcion |
|---|---|
os.O_RDONLY | Solo lectura |
os.O_WRONLY | Solo escritura |
os.O_RDWR | Lectura y escritura |
os.O_CREATE | Crear si no existe |
os.O_APPEND | Agregar al final |
os.O_TRUNC | Truncar al abrir |
io.Copy
Copia datos de un Reader a un Writer de forma eficiente.
package main
import (
"fmt"
"io"
"os"
)
func main() {
origen, err := os.Open("original.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer origen.Close()
destino, err := os.Create("copia.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
defer destino.Close()
bytes, err := io.Copy(destino, origen)
if err != nil {
fmt.Println("Error copiando:", err)
return
}
fmt.Printf("Copiados %d bytes\n", bytes)
}
bytes.Buffer
Buffer en memoria que implementa tanto Reader como Writer.
package main
import (
"bytes"
"fmt"
)
func main() {
var buf bytes.Buffer
// Escribir
buf.WriteString("Hola ")
buf.WriteString("Mundo")
fmt.Fprintf(&buf, " %d", 2024)
// Leer
fmt.Println(buf.String()) // Hola Mundo 2024
fmt.Println(buf.Len()) // 15
}
filepath: rutas multiplataforma
package main
import (
"fmt"
"path/filepath"
)
func main() {
// Construir rutas de forma segura
ruta := filepath.Join("home", "user", "docs", "archivo.txt")
fmt.Println(ruta) // home/user/docs/archivo.txt
// Extraer partes
fmt.Println(filepath.Dir(ruta)) // home/user/docs
fmt.Println(filepath.Base(ruta)) // archivo.txt
fmt.Println(filepath.Ext(ruta)) // .txt
// Ruta absoluta
abs, _ := filepath.Abs(".")
fmt.Println("CWD:", abs)
}
filepath.Walk y WalkDir
package main
import (
"fmt"
"io/fs"
"path/filepath"
)
func main() {
// WalkDir es mas eficiente que Walk (Go 1.16+)
err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
fmt.Println("[DIR]", path)
} else {
info, _ := d.Info()
fmt.Printf(" %s (%d bytes)\n", path, info.Size())
}
return nil
})
if err != nil {
fmt.Println("Error:", err)
}
}
Argumentos de linea de comandos
os.Args
package main
import (
"fmt"
"os"
)
func main() {
// os.Args[0] es el nombre del programa
fmt.Println("Programa:", os.Args[0])
if len(os.Args) < 2 {
fmt.Println("Uso: programa <nombre>")
os.Exit(1)
}
fmt.Println("Hola,", os.Args[1])
}
Package flag
package main
import (
"flag"
"fmt"
)
func main() {
nombre := flag.String("nombre", "mundo", "nombre a saludar")
veces := flag.Int("veces", 1, "repeticiones del saludo")
verbose := flag.Bool("v", false, "modo verbose")
flag.Parse()
for i := 0; i < *veces; i++ {
if *verbose {
fmt.Printf("[%d] ", i+1)
}
fmt.Printf("Hola, %s!\n", *nombre)
}
// Argumentos restantes (no-flag)
fmt.Println("Args extra:", flag.Args())
}
go run main.go -nombre=Go -veces=3 -v extra1 extra2
# [1] Hola, Go!
# [2] Hola, Go!
# [3] Hola, Go!
# Args extra: [extra1 extra2]
Variables de entorno
package main
import (
"fmt"
"os"
)
func main() {
// Leer variable
home := os.Getenv("HOME")
fmt.Println("HOME:", home)
// Leer con valor por defecto
puerto := os.Getenv("PORT")
if puerto == "" {
puerto = "8080"
}
// LookupEnv distingue vacio de no definido
val, existe := os.LookupEnv("MI_VAR")
if !existe {
fmt.Println("MI_VAR no esta definida")
} else {
fmt.Println("MI_VAR:", val)
}
// Establecer variable (para el proceso actual)
os.Setenv("APP_MODE", "development")
}
Operaciones de directorio
package main
import (
"fmt"
"os"
)
func main() {
// Crear directorio
os.Mkdir("datos", 0755)
// Crear directorios anidados (como mkdir -p)
os.MkdirAll("datos/2024/enero", 0755)
// Eliminar archivo o directorio vacio
os.Remove("archivo.tmp")
// Eliminar directorio con contenido (como rm -rf)
os.RemoveAll("datos")
// Renombrar/mover
os.Rename("viejo.txt", "nuevo.txt")
// Directorio temporal
dir, err := os.MkdirTemp("", "miapp-*")
if err != nil {
fmt.Println("Error:", err)
return
}
defer os.RemoveAll(dir)
fmt.Println("Dir temporal:", dir)
}