io.MultiReader în Go

Leagă mai multe Reader-e unul după altul, ca și cum ar fi o singură sursă continuă.

Sintaxă
func MultiReader(readers ...Reader) Reader

Ce face io.MultiReader

io.MultiReader citește întâi tot din primul Reader, apoi din al doilea și așa mai departe; io.EOF apare abia după ultimul. Datele nu sunt copiate dinainte: fiecare sursă este citită când îi vine rândul.

Exemple: un antet adăugat înaintea unui fișier, mai multe fișiere mici trimise ca unul singur, date deja citite (de exemplu primii octeți folosiți pentru a ghici tipul fișierului) puse la loc înaintea restului.

Parametri

Parametru Descriere
readers Sursele, în ordine.

Ce returnează

Un io.Reader.

Exemple

Exemplul 1: Antet + date

package main

import (
    "io"
    "os"
    "strings"
)

func main() {
    antet := strings.NewReader("nume,oras\n")
    date := strings.NewReader("Ana,Iași\nMihai,Cluj\n")
    io.Copy(os.Stdout, io.MultiReader(antet, date))
}

Rezultat

nume,oras
Ana,Iași
Mihai,Cluj

Exemplul 2: Mai multe fișiere ca o singură sursă

package main

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

func main() {
    os.WriteFile("ianuarie.txt", []byte("120\n80\n"), 0o644)
    os.WriteFile("februarie.txt", []byte("200\n"), 0o644)

    var surse []io.Reader
    for _, nume := range []string{"ianuarie.txt", "februarie.txt"} {
        f, err := os.Open(nume)
        if err != nil {
            fmt.Println("eroare:", err)
            return
        }
        defer f.Close()
        surse = append(surse, f)
    }

    s := bufio.NewScanner(io.MultiReader(surse...))
    total := 0
    for s.Scan() {
        var x int
        fmt.Sscan(s.Text(), &x)
        total += x
    }
    fmt.Println("total vânzări:", total, "lei")
}

Rezultat

total vânzări: 400 lei

Exemplul 3: Octeți citiți puși la loc

Citim primii 4 octeți ca să recunoaștem un PDF, apoi îi punem înapoi în fața restului, ca sursa să fie completă.

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    var r io.Reader = strings.NewReader("%PDF-1.7 restul documentului")
    inceput := make([]byte, 4)
    io.ReadFull(r, inceput)
    fmt.Println("este PDF:", string(inceput) == "%PDF")

    complet, _ := io.ReadAll(io.MultiReader(strings.NewReader(string(inceput)), r))
    fmt.Printf("%q\n", complet)
}

Rezultat

este PDF: true
"%PDF-1.7 restul documentului"

Greșeli frecvente

  • Adaugi același Reader de două ori, așteptând să fie citit de două ori. A doua oară este deja la final și nu mai dă nimic.

Comenzi înrudite

  • io.MultiWriter: Creează un Writer care trimite fiecare scriere la toți Writer-ii dați, ca tee din Unix.
  • io.Reader: Interfața pentru orice sursă de date din care se poate citi: fișiere, text, conexiuni, răspunsuri HTTP.
  • io.Copy: Copiază toate datele dintr-un Reader într-un Writer, pe bucăți, fără să le țină pe toate în memorie.
  • io.TeeReader: Returnează un Reader care, pe măsură ce citești din r, scrie aceiași octeți și în w.