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.

Go whitespace.go
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")
}
Output
HalloWelt
Port=8080
42 true
x=42 trueende

Beachte 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

FunktionSpaces zwischen OperandenNewline am EndeFormat-Kontrolle
Fprintnur zwischen zwei Nicht-Stringsneinnein
Fprintlnimmer zwischen allen Operandenjanein
Fprintfexakt wie im Format-String festgelegtnur via \nja
Go vergleich.go
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)
}
Output
api8080 true
api 8080 true
api:8080 up=true

In 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.

Go readyfile.go
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)
}
Output
geschrieben: 5 Bytes nach /tmp/service.ready

Die 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.

Go health.go
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))
}
Output
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.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Das fmt-Paket — Formatierte I/O

Zur Übersicht