strings.Builder în Go

Construiește eficient un text din multe bucăți: adaugi cu WriteString, WriteByte, WriteRune sau Write și obții rezultatul cu String().

Sintaxă
var sb strings.Builder
func (b *Builder) WriteString(s string) (int, error)
func (b *Builder) WriteByte(c byte) error
func (b *Builder) WriteRune(r rune) (int, error)
func (b *Builder) Write(p []byte) (int, error)
func (b *Builder) String() string

Ce face strings.Builder

Textele din Go nu pot fi modificate, deci s += "x" creează la fiecare pas un text nou și copiază tot ce era înainte. Într-o buclă cu mii de pași, asta devine lent. strings.Builder ține un buffer care crește și adaugă bucățile la final fără copieri inutile.

Nu trebuie inițializat: var sb strings.Builder este gata de folosit. Adaugi cu WriteString(text), WriteByte('\n') (un octet ASCII), WriteRune('ș') (un caracter Unicode) sau Write([]byte). La final, sb.String() returnează textul construit, fără să-l copieze.

*strings.Builder implementează io.Writer, deci poți scrie în el și cu fmt.Fprintf(&sb, ...). Metodele de scriere returnează o eroare doar pentru a respecta interfețele; eroarea este întotdeauna nil.

Parametri

Parametru Descriere
s / c / r / p Bucata adăugată: text (string), octet (byte), caracter (rune) sau octeți ([]byte).

Ce returnează

Metodele Write* returnează numărul de octeți adăugați și o eroare mereu nil (WriteByte doar eroarea). String() returnează textul construit.

Exemple

Exemplul 1: Construirea unui text pas cu pas

package main

import (
	"fmt"
	"strings"
)

func main() {
	var sb strings.Builder
	sb.WriteString("Orașe: ")
	for i, oras := range []string{"Iași", "Cluj", "Brașov"} {
		if i > 0 {
			sb.WriteString(", ")
		}
		sb.WriteString(oras)
	}
	sb.WriteByte('.')
	sb.WriteByte('\n')
	sb.WriteRune('✓')
	sb.WriteRune('ș')
	sb.Write([]byte(" gata"))
	fmt.Println(sb.String())
}

Rezultat

Orașe: Iași, Cluj, Brașov.
✓ș gata

Exemplul 2: Fprintf direct în Builder

Dăm &sb (adresa), pentru că metodele lui Builder au receptor pointer.

package main

import (
	"fmt"
	"strings"
)

func main() {
	var sb strings.Builder
	fmt.Fprintf(&sb, "%-6s %4s\n", "Elev", "Nota")
	note := map[string]int{"Ana": 10, "Ion": 8}
	for _, nume := range []string{"Ana", "Ion"} {
		fmt.Fprintf(&sb, "%-6s %4d\n", nume, note[nume])
	}
	fmt.Print(sb.String())
	fmt.Println("lungime:", sb.Len(), "octeți")
}

Rezultat

Elev   Nota
Ana      10
Ion       8
lungime: 36 octeți

Exemplul 3: Cât de lent este += față de Builder

Ambele variante dau același text. Diferența este numărul de copieri: cu += fiecare pas copiază tot textul de până atunci. Rezultatul afișat este doar lungimea, ca să fie același la fiecare rulare.

package main

import (
	"fmt"
	"strings"
)

func main() {
	s := ""
	for i := 0; i < 1000; i++ {
		s += "ab" // creează 1000 de texte noi
	}

	var sb strings.Builder
	for i := 0; i < 1000; i++ {
		sb.WriteString("ab") // adaugă în același buffer
	}
	fmt.Println(len(s), sb.Len(), s == sb.String())
}

Rezultat

2000 2000 true

Greșeli frecvente

  • Copiezi un Builder deja folosit (b2 := sb) și scrii în copie. Programul se oprește cu panic: strings: illegal use of non-zero Builder copied by value. Transmite-l mereu prin pointer (*strings.Builder).
  • Scrii fmt.Fprintf(sb, …) fără &: nu compilează, pentru că doar *strings.Builder este io.Writer.
  • Folosești WriteByte('ș'). ș nu încape într-un octet (nu compilează); pentru diacritice folosește WriteRune sau WriteString.

Comenzi înrudite

  • strings.Builder.Grow: Grow rezervă loc dinainte, Len dă octeții scriși, Cap capacitatea bufferului, iar Reset golește Builder-ul.
  • strings.Join: Lipește textele dintr-un slice într-un singur text, cu un separator între ele.
  • bytes.Buffer: Un buffer de octeți care crește singur: scrii în el cu Write/WriteString/WriteByte/WriteRune și citești cu String() sau Bytes().
  • fmt.Fprintln: Scrie valorile într-un io.Writer (fișier, buffer, răspuns HTTP) cu regulile lui Println, respectiv Print.