Go ofrece varias capas de abstracción para leer y escribir ficheros. os.ReadFile y os.WriteFile son convenientes para ficheros pequeños. bufio.Scanner y bufio.Writer añaden buffering para eficiencia con ficheros grandes. io.Copy transfiere datos entre cualquier par de Reader y Writer sin cargar todo en memoria.
Leer un fichero completo
package main
import (
"fmt"
"os"
)
func main() {
// lee todo el fichero en memoria de una vez (ok para ficheros pequeños)
contenido, err := os.ReadFile("datos.txt")
if err != nil {
fmt.Fprintf(os.Stderr, "error leyendo: %vn", err)
os.Exit(1)
}
fmt.Println(string(contenido))
}
Escribir un fichero completo
datos := []byte("línea unonlínea dosnlínea tresn")
// 0644 = permisos rw-r--r--
err := os.WriteFile("salida.txt", datos, 0644)
if err != nil {
panic(err)
}
Leer línea a línea con bufio.Scanner
Para ficheros grandes que no caben en memoria, lee línea a línea:
func contarLineas(ruta string) (int, error) {
f, err := os.Open(ruta)
if err != nil {
return 0, fmt.Errorf("abrir %s: %w", ruta, err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
lineas := 0
for scanner.Scan() {
lineas++
// scanner.Text() contiene la línea sin el n
}
if err := scanner.Err(); err != nil {
return lineas, fmt.Errorf("escanear %s: %w", ruta, err)
}
return lineas, nil
}
Por defecto, Scanner divide por líneas. Si necesitas dividir por tokens o bytes, usa scanner.Split(bufio.ScanWords) u otras funciones de split.
Escribir con bufio.Writer
bufio.Writer acumula los datos en un buffer interno y los escribe en bloques. Llama siempre a Flush() al final para vaciar el buffer:
func escribirCSV(ruta string, filas [][]string) error {
f, err := os.Create(ruta)
if err != nil {
return err
}
defer f.Close()
w := bufio.NewWriter(f)
for _, fila := range filas {
fmt.Fprintln(w, strings.Join(fila, ","))
}
return w.Flush() // importante: vacía el buffer al disco
}
io.Copy: copiar entre cualquier Reader y Writer
func copiarFichero(src, dst string) error {
origen, err := os.Open(src)
if err != nil {
return err
}
defer origen.Close()
destino, err := os.Create(dst)
if err != nil {
return err
}
defer destino.Close()
_, err = io.Copy(destino, origen)
return err
}
io.Copy funciona con cualquier tipo que implemente io.Reader o io.Writer: ficheros, conexiones de red, buffers en memoria, respuestas HTTP...
filepath.Join para rutas portables
import "path/filepath"
// en Linux/Mac usa /, en Windows usa
ruta := filepath.Join("datos", "2026", "ventas.csv")
// obtener el directorio o el nombre del fichero
dir := filepath.Dir(ruta) // "datos/2026"
base := filepath.Base(ruta) // "ventas.csv"
ext := filepath.Ext(ruta) // ".csv"
Crear directorios
// crear un directorio
err := os.Mkdir("logs", 0755)
// crear la jerarquía completa (como mkdir -p)
err = os.MkdirAll("datos/2026/07", 0755)
// listar el contenido de un directorio
entradas, err := os.ReadDir("datos")
for _, e := range entradas {
fmt.Printf("%s (dir: %v)n", e.Name(), e.IsDir())
}
Ficheros temporales
// crea un fichero temporal en el directorio de temporales del sistema
tmp, err := os.CreateTemp("", "proceso-*.json")
if err != nil {
panic(err)
}
defer os.Remove(tmp.Name()) // limpia al terminar
fmt.Fprintln(tmp, `{"estado":"procesando"}`)
tmp.Close()
