π Confronto metodi di bufio.Reader
in Go
Introduzione
Recentemente sto studiando Go, linguaggio interessante e per me Γ¨ un ritorno (ho iniziato a programmare col C) ad un tipo di programmazione profondamente diversa da quella che ho praticato negli ultimi anni specializzandomi su PHP/Laravel.
Una delle cose che mi interessano Γ¨ come leggere e scrivere da uno stream quindi, con l’aiuto di una IA mi sono fatto un veloce riepilogo delle differenze tra alcuni metodi della libreria bufio
.
I metodi ReadRune
, ReadBytes
, ReadString
e ReadLine
sono molto simili ma solo in apparenza.
Ecco una guida rapida per capire le differenze.
Tabella comparativa
Metodo | Firma | Cosa legge | Tipo restituito | Include \n ? | Note pratiche |
---|---|---|---|---|---|
ReadRune() | (r rune, size int, err error) | Un singolo carattere Unicode (1 rune) | rune (+size+err) | N/A (legge solo 1 carattere) | Utile per analizzare caratteri uno alla volta (UTF-8 safe). |
ReadBytes(delim byte) | ([]byte, error) | Fino a delim incluso | []byte | β sΓ¬ | Restituisce slice di byte, ottimo per parsing binario o testo grezzo. |
ReadString(delim byte) | (string, error) | Fino a delim incluso | string | β sΓ¬ | Comodo se sai di leggere testo, meno efficiente di ReadBytes . |
ReadLine() | ([]byte, isPrefix bool, error) | Una riga (senza \n ) | []byte + isPrefix + err | β no | Se la riga Γ¨ troppo lunga, la restituisce a pezzi (isPrefix = true ). Serve loop per ricomporla. PiΓΉ basso livello. |
π Diagramma di flusso decisionale
ββββββββββββββββββββββββββββββββ
β Vuoi leggere 1 carattere? β
βββββββββββββββββ¬βββββββββββββββ
β
βββββββββββΌββββββββββ
β Usa ReadRune() β
βββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββ
β Vuoi leggere fino a un β
β delimitatore (es. '\n')? β
βββββββββββββββββ¬βββββββββββββββ
β
βββββββββββββββββββΌββββββββββββββββββ
β | β
ββββββββββββΌβββββββββββ | ββββββββββββΌββββββββββββ
β Ti serve un []byte? β | β Ti serve una stringa?β
ββββββββββββ¬βββββββββββ | βββββββββββββ¬βββββββββββ
β | β
ββββββββββββΌβββββββββββ | βββββββββββββΌββββββββββββ
β Usa ReadBytes() β | β Usa ReadString() β
βββββββββββββββββββββββ | βββββββββββββββββββββββββ
β
β
βΌ
ββββββββββββββββββββββββββββββββ
β Devi gestire linee molto β
β lunghe (buffer a pezzi)? β
βββββββββββββββββ¬βββββββββββββββ
β
βββββββββββΌββββββββββ
β Usa ReadLine() β
βββββββββββββββββββββ
Esempio pratico
package main
import (
"bufio"
"bytes"
"fmt"
"strings"
)
func main() {
text := "cafΓ©\nseconda linea\nterza linea senza newline finale"
// --- ReadRune(): legge un singolo carattere Unicode alla volta ---
fmt.Println("== ReadRune() ==")
r := bufio.NewReader(strings.NewReader(text))
for i := 0; i < 4; i++ { // leggo le prime 4 rune: c, a, f, Γ©
ru, size, err := r.ReadRune()
if err != nil {
fmt.Println("Errore ReadRune:", err)
break
}
fmt.Printf("rune=%q size=%d\n", ru, size)
}
fmt.Println()
// --- ReadBytes('\n'): legge fino al delimitatore incluso e restituisce []byte ---
fmt.Println("== ReadBytes('\\n') ==")
r = bufio.NewReader(strings.NewReader(text))
b, err := r.ReadBytes('\n')
if err != nil {
fmt.Println("Errore ReadBytes:", err)
}
fmt.Printf("ReadBytes: %q (len=%d)\n", b, len(b))
fmt.Println()
// --- ReadString('\n'): stesso comportamento ma restituisce string ---
fmt.Println("== ReadString('\\n') ==")
r = bufio.NewReader(strings.NewReader(text))
s, err := r.ReadString('\n')
if err != nil {
fmt.Println("Errore ReadString:", err)
}
fmt.Printf("ReadString: %q (len=%d)\n", s, len(s))
fmt.Println()
// --- ReadLine(): NON include '\n' e puΓ² restituire porzioni (isPrefix=true) ---
// Forziamo una linea piΓΉ lunga del buffer per vedere isPrefix=true.
fmt.Println("== ReadLine() con buffer piccolo e isPrefix ==")
longLine := "abcdefghij\n" // 10 caratteri + '\n'
smallBuf := bufio.NewReaderSize(strings.NewReader(longLine), 8)
var parts [][]byte
for {
line, isPrefix, err := smallBuf.ReadLine()
if err != nil {
// fine input o errore
break
}
// Copia difensiva del frammento letto
parts = append(parts, append([]byte(nil), line...))
fmt.Printf("chunk=%q isPrefix=%v\n", line, isPrefix)
if !isPrefix {
break
}
}
recombined := bytes.Join(parts, nil)
fmt.Printf("Ricomposta: %q (pezzi=%d)\n", recombined, len(parts))
fmt.Println()
// --- Esempio classico di ReadLine su testo normale (senza superare il buffer) ---
fmt.Println("== ReadLine() normale ==")
r = bufio.NewReader(strings.NewReader(text))
line, isPrefix, err := r.ReadLine()
if err == nil {
fmt.Printf("line=%q isPrefix=%v (niente '\\n')\n", line, isPrefix)
}
}
Conclusioni
In sintesi veloce:
ReadRune
β analisi carattere-per-carattere, sicuro su UTF-8.ReadBytes
β legge fino al delimitatore e restituisce[]byte
(ottimo per parsing binario/grezzo).ReadString
β come sopra ma restituisce string (piΓΉ comodo, piΓΉ allocazioni).ReadLine
β non include\n
, gestisce linee molto lunghe a pezzi conisPrefix
.
Insidie comuni da ricordare:
- Fine riga su Windows Γ¨
\r\n
: rimuovi il\r
finale se leggi fino a\n
. bufio.Scanner
ha un limite di token (β64 KB) che va aumentato per righe grandi.- Le allocazioni:
ReadString
crea sempre una nuova stringa; conReadBytes
puoi restare su[]byte
finchΓ© serve.
Cosa usare quando:
- Parsing binario o attenzione alle allocazioni β
ReadBytes
. - Input testuale βline-orientedβ β
ReadString
(oppure Scanner). - Analisi fine di rune (accenti/emoji) β
ReadRune
. - Log/file con righe enormi β
ReadLine
(ricomponi quandoisPrefix
== true).