fmt.Fprint ist die Verallgemeinerung von fmt.Print auf einen beliebigen io.Writer. Statt fest nach os.Stdout zu schreiben, übergibst du das Schreibziel explizit als erstes Argument — danach folgen die Operanden, die nach derselben Heuristik wie bei Print zusammengefügt werden: Leerzeichen werden nur zwischen zwei aufeinanderfolgenden Nicht-String-Operanden eingefügt, und am Ende hängt die Funktion kein Newline an.
Diese Kombination — frei wählbarer Writer, positionale Argumente, keine automatische Zeilentrennung — macht Fprint zur Wahl, wenn du Print-artige Ausgabe in eine Datei, einen bytes.Buffer, eine Netzwerkverbindung oder einen http.ResponseWriter lenkst und der Output bewusst ohne Trailing-Newline enden soll.
Signatur
func Fprint(w io.Writer, a ...any) (n int, err error) — Writer als erstes Argument, dann variadische Operanden. Der Rückgabewert n zählt die geschriebenen Bytes, err propagiert Fehler des darunterliegenden Writers — bei Print und Println ist dieser Fehler praktisch nie relevant, bei Fprint dagegen schon, weil Dateien, Sockets oder gekappte Pipes echte I/O-Fehler liefern können.
Whitespace-Regel (geerbt von Print)
Die wichtigste — und überraschendste — Eigenschaft von Fprint ist die Spacing-Heuristik: zwischen zwei Operanden wird nur dann ein Leerzeichen eingefügt, wenn beide keine Strings sind. Sobald einer der beiden Nachbarn ein String ist, klebt die Ausgabe ohne Trennzeichen zusammen.
package main
import (
"fmt"
"os"
)
func main() {
fmt.Fprint(os.Stdout, "Hallo", "Welt")
fmt.Fprint(os.Stdout, "\n")
fmt.Fprint(os.Stdout, "Port=", 8080)
fmt.Fprint(os.Stdout, "\n")
fmt.Fprint(os.Stdout, 42, true)
fmt.Fprint(os.Stdout, "\n")
fmt.Fprint(os.Stdout, "x=", 42, true, "ende")
fmt.Fprint(os.Stdout, "\n")
}HalloWelt
Port=8080
42 true
x=42 trueendeBeachte den letzten Fall: zwischen 42 und true steht ein Leerzeichen (beide Nicht-Strings), zwischen true und "ende" jedoch nicht (rechts ist String). Wenn du konsistente Trennung willst, ist Fprintln (immer Spaces) oder Fprintf mit explizitem Format-String die robustere Wahl.
Kein Newline am Ende
Fprint hängt nichts an die Ausgabe an — kein \n, kein Whitespace, keine Sentinel-Bytes. Nach dem letzten Operanden steht der Schreibcursor unmittelbar dahinter. Wenn du Zeilenende-Semantik brauchst, wechsle zu Fprintln. Für gezielte Newlines an exakter Stelle gib "\n" selbst als String-Operand mit, oder formatiere mit Fprintf und \n im Format-String.
Vergleich zu Fprintln und Fprintf
| Funktion | Spaces zwischen Operanden | Newline am Ende | Format-Kontrolle |
|---|---|---|---|
Fprint | nur zwischen zwei Nicht-Strings | nein | nein |
Fprintln | immer zwischen allen Operanden | ja | nein |
Fprintf | exakt wie im Format-String festgelegt | nur via \n | ja |
package main
import (
"fmt"
"os"
)
func main() {
name, port := "api", 8080
ok := true
fmt.Fprint(os.Stdout, name, port, ok)
fmt.Fprint(os.Stdout, "\n")
fmt.Fprintln(os.Stdout, name, port, ok)
fmt.Fprintf(os.Stdout, "%s:%d up=%t\n", name, port, ok)
}api8080 true
api 8080 true
api:8080 up=trueIn Zeile 1 (Fprint) klebt api direkt an 8080, weil api ein String ist. Fprintln setzt überall Spaces und ein abschließendes Newline. Fprintf gibt dir mit %s:%d die volle Kontrolle.
Wann Fprint Sinn macht
Fprint ist die mit Abstand seltenste der drei Writer-Funktionen. Es gibt aber drei Nischen, in denen Fprint tatsächlich die passende Wahl ist. Erstens Sentinel-Tokens ohne Trailing-Newline: Lock-Dateien, PID-Files oder Marker-Dateien, deren Inhalt von anderen Tools byte-genau verglichen wird. Zweitens HTTP-Bodies für extrem schlanke Status-Endpoints, die nur einen einzigen konkatenierten Wert ausgeben sollen. Drittens Pipe-Protokolle, in denen das Trennzeichen vom Empfänger definiert wird.
Sentinel-Token in eine Datei schreiben
Ein typisches Muster: Ein Daemon legt nach erfolgreichem Bootstrap eine Status-Datei an, deren reiner Inhalt READY lautet — ohne Whitespace, ohne Newline. Ein Watchdog vergleicht den Dateiinhalt byte-exakt mit dem erwarteten Token, und ein versehentliches \n würde den Vergleich brechen.
package main
import (
"fmt"
"log"
"os"
)
func writeReadyFile(path string) error {
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("create ready file: %w", err)
}
defer f.Close()
if _, err := fmt.Fprint(f, "READY"); err != nil {
return fmt.Errorf("write token: %w", err)
}
return nil
}
func main() {
const path = "/tmp/service.ready"
if err := writeReadyFile(path); err != nil {
log.Fatal(err)
}
info, _ := os.Stat(path)
fmt.Printf("geschrieben: %d Bytes nach %s\n", info.Size(), path)
}geschrieben: 5 Bytes nach /tmp/service.readyDie os.Stat-Prüfung bestätigt, dass exakt fünf Bytes geschrieben wurden — kein sechstes \n. Beachte das Fehler-Wrapping mit %w: weil Fprint einen echten error zurückgibt, gehört dieser Fehler in deine Wrapper-Kette.
HTTP-Body ohne Newlines
Minimalistische HTTP-Endpoints, deren Body von einem Client byte-genau geparst wird — etwa ein Health-Check, dessen Body ausschließlich eine Versions-ID enthält ohne Trennzeichen über die Fprint-Heuristik hinaus.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprint(w, "svc=", "api", 42, true)
}
func main() {
srv := httptest.NewServer(http.HandlerFunc(healthHandler))
defer srv.Close()
resp, _ := http.Get(srv.URL + "/health")
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("body: %q (len=%d)\n", string(body), len(body))
}body: "svc=api42 true" (len=14)Das Ergebnis illustriert die Whitespace-Regel im Realeinsatz: zwischen "svc=" und "api" keine Lücke, zwischen "api" und 42 ebenfalls nicht, aber zwischen 42 und true entsteht ein Space. Die Body-Länge ist exakt 14 Bytes — kein abschließendes \n.
Interessantes
Writer kommt zuerst
Der io.Writer ist immer das erste Argument von Fprint, danach folgen beliebig viele Operanden.
Whitespace nur zwischen Nicht-Strings
Fprint setzt ein Leerzeichen ausschließlich zwischen zwei aufeinanderfolgenden Operanden, die beide keine Strings sind.
Kein automatisches Newline
Fprint hängt nichts an die Ausgabe an. Der nächste Schreibvorgang setzt unmittelbar hinter dem letzten Byte fort.
Error-Rückgabe ernst nehmen
Anders als bei Print auf Stdout sind die Fehler bei Fprint real: Dateien können vollaufen, Sockets brechen, http.ResponseWriter schlägt fehl.
Fprintln für Auto-Spaces und Newline
Wenn du sowohl konsistente Trennung als auch Zeilenende willst, ist Fprintln die robustere Wahl.
Fprintf wenn Format-Verben nötig
Sobald du Platzhalter wie %d, %v, %q brauchst, ist Fprintf das Werkzeug der Wahl — Fprint kennt keine Verben.
Intern wird %v angewendet
Fprint formatiert jeden Operanden wie mit %v: Standard-Repräsentation, Stringer-Methoden werden respektiert.
Selten die richtige Wahl
In der Praxis ist Fprint deutlich seltener sinnvoll als Fprintln oder Fprintf — wähle es nur bewusst.