io y bufio en Go: leer y escribir ficheros de forma eficiente

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

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP