Esta pagina se ve mejor con JavaScript habilitado

Go reading files

 ·  🎃 kr0m

Leer ficheros es una de las operaciones mas comunes en Go, en este artículo aprenderemos las distintas posibilidades que nos ofrece para hacerlo.

El artículo se compone de varias secciones:


Si eres nuevo en el mundo de Go te recomiendo los siguientes artículos anteriores:


Cargar fichero entero en memoria:

La manera mas sencilla de leer ficheros es cargarlos directamente en RAM utilizando la función os.ReadFile(), esta permite indicar el fichero tanto desde la ruta relativa como la absoluta.

package main

import (
    "fmt"
    "os"
)

func main() {
    contents, err := os.ReadFile("test.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(contents))

    contents, err = os.ReadFile("/home/kr0m/test/test.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(contents))
}
Contents of file: This is a test file content

Contents of file: This is a test file content

Leer fichero por partes:

Si nuestro equipo tiene unos recursos muy limitados o si el fichero a leer es extremadamente grande, puede interesarnos cargar el contenido del fichero por partes para no agotar la memoria RAM disponible en el sistema.

Básicamente lo que hacemos es:

  • Abrir el fichero.
  • Si no hay errores, dejamos un cierre de fichero mediante defer.
  • Creamos un buffer reader del que leeremos de len(b) en len(b) bytes, la variable n indicará el número de bytes leídos del buffer.
  • Si hemos llegado al final del fichero dejamos de leer.
  • Si se ha producido un error sin ser final de fichero, avisamos del error ocurrido.
  • Si no se han producido errores, mostramos los n bytes del slice b.
package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "os"
)

func main() {
    f, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err = f.Close(); err != nil {
            log.Fatal(err)
        }
    }()

    r := bufio.NewReader(f)
    b := make([]byte, 3)
    for {
        n, err := r.Read(b)
        if err == io.EOF {
            fmt.Println("Finished reading file")
            break
        }
        if err != nil {
            fmt.Printf("Error %s reading file", err)
            break
        }
        fmt.Println(string(b[0:n]))
    }
}
lin
e1

lin
e2

lin
e3

lin
e4

lin
e5

Finished reading file

Leer fichero línea a línea:

Una de las formas mas comunes de acceder al contenido de un fichero es leyéndolo línea a línea, los pasos son muy similares al caso anterior pero en esta ocasión no utilizamos un buffer reader, si no un buffer scanner del que leemos las líneas del fichero y finalmente comprobamos si se ha producido algún error mientras leíamos.

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    f, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err = f.Close(); err != nil {
            log.Fatal(err)
        }
    }()

    s := bufio.NewScanner(f)
    for s.Scan() {
        fmt.Println(s.Text())
    }

    err = s.Err()
    if err != nil {
        log.Fatal(err)
    }
}
line1
line2
line3
line4
line5

Embeber fichero en el binario de Go:

En Go cabe la posibilidad de embeber ficheros dentro del propio binario, esto no se limita a ficheros de texto, pueden ser incluso otros binarios, tan solo debemos indicarlo mediante //go:embed justo antes de la variable que utilizaremos para guardar dicho contenido. Pero debemos tener en cuenta que no es una forma segura de ocultar ningún tipo de información, tan solo es una forma cómoda de asegurarse de que nuestro software dispone de todo lo necesario para funcionar sin depender de ficheros externos.

package main

import (
    _ "embed"
    "fmt"
)

//go:embed test.txt
var contents []byte

func main() {
    fmt.Println("Contents of file:", string(contents))
}
echo SUPERSECRET_PASSWORD > test.txt
go build test.go
rm test.txt

Al ejecutar el binario podemos ver que el contenido del fichero sigue dentro del binario.

./test
Contents of file: SUPERSECRET_PASSWORD

Podemos ver el contenido en el binario de forma externa mediante el comando strings.

strings test|grep SUPERSECRET
SUPERSECRET_PASSWORD
Si te ha gustado el artículo puedes invitarme a un RedBull aquí