strings.LastIndexAny liefert den Byte-Offset des letzten Zeichens aus einem Cutset chars, das in s vorkommt. Die Funktion arbeitet rune-aware: chars wird als Menge von Unicode-Codepoints interpretiert, nicht als Bytefolge. Damit lassen sich rechtsbündige Trennzeichen-Suchen mit mehreren Kandidaten in einem einzigen Aufruf erledigen — typischerweise dann, wenn das relevante Trennzeichen näher am Ende der Eingabe erwartet wird (Dateinamen, Pfade, Versions-Strings). Bei keinem Treffer oder leerem Cutset ist der Rückgabewert -1.

Die Signatur ist symmetrisch zu IndexAny, dreht aber die Suchrichtung um. Es gibt keinen Startindex-Parameter — die Suche beginnt immer am rechten Stringende und wandert rückwärts zur ersten gefundenen Rune.

Go signatur.go
func LastIndexAny(s, chars string) int

Der zurückgegebene int ist ein Byte-Offset in s, kein Rune-Index. Das ist wichtig, sobald s Multi-Byte-Sequenzen enthält, denn dann fallen Rune-Position und Byte-Offset auseinander.

LastIndexAny scannt s von hinten nach vorne und gibt den Offset der ersten Rune zurück, deren Codepoint im Cutset enthalten ist. Im Vergleich zu IndexAny zeigt sich der Unterschied am gleichen Eingabestring sofort: der erste Treffer von links ist nicht dasselbe wie der letzte von rechts.

Go richtung.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "user@host.example.com"
	fmt.Println(strings.IndexAny(s, "@."))     // erstes @ oder .
	fmt.Println(strings.LastIndexAny(s, "@.")) // letztes @ oder .
}
Output
4
16

Hier trifft IndexAny das @ an Position 4, während LastIndexAny den letzten Punkt vor com an Byte-Offset 16 findet. Der Cutset bleibt identisch — nur die Richtung entscheidet, welche Rune zurückgemeldet wird.

Findet LastIndexAny eine Rune, die in UTF-8 aus mehreren Bytes besteht, zeigt der Rückgabewert auf das erste Byte dieser Sequenz. Damit lässt sich der Treffer direkt als Slice-Grenze verwenden, ohne zusätzliche Korrektur.

Go multibyte.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	s := "café — bäckerei"
	idx := strings.LastIndexAny(s, "äé")
	fmt.Println(idx)
	fmt.Println(s[idx:])
}
Output
11
äckerei

Das ä belegt zwei Bytes (0xC3 0xA4), der Offset 11 markiert dessen erstes Byte. s[idx:] liefert deshalb einen syntaktisch korrekten UTF-8-Substring ab dem Treffer und nicht etwa ein zerbrochenes Surrogat.

Ein leerer Cutset enthält keine Codepoints, also kann nichts gematcht werden — das Ergebnis ist -1. Das gleiche gilt für ein leeres s, weil es nichts zum Durchsuchen gibt.

Go leer.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.LastIndexAny("abc", ""))
	fmt.Println(strings.LastIndexAny("", "abc"))
	fmt.Println(strings.LastIndexAny("", ""))
}
Output
-1
-1
-1

Damit verhält sich LastIndexAny konsistent zu IndexAny und braucht keinen Spezialfall im aufrufenden Code: ein -1-Check deckt alle Leer-Varianten ab.

Die drei Funktionen wirken auf den ersten Blick verwandt, lösen aber jeweils ein anderes Problem. Die folgende Tabelle stellt Richtung und Treffer-Definition gegenüber.

FunktionSucht nachRichtungRückgabe
IndexAny(s, chars)beliebige Rune aus charsvon linksByte-Offset des ersten Treffers / -1
LastIndexAny(s, chars)beliebige Rune aus charsvon rechtsByte-Offset des letzten Treffers / -1
LastIndex(s, substr)feste Bytefolge substrvon rechtsByte-Offset des letzten Vorkommens / -1

LastIndex arbeitet auf Substring-Ebene, LastIndexAny auf Einzel-Rune-Ebene mit Cutset-Logik. Wer mehrere mögliche Trennzeichen rechtsbündig sucht, spart sich mit LastIndexAny eine Schleife aus LastIndex-Aufrufen samt Maximum-Berechnung.

Plattformübergreifende Pfad-Parser müssen sowohl / als auch \ als Trennzeichen akzeptieren. Mit LastIndexAny findet man das letzte Trennzeichen in einem Aufruf und kann den Verzeichnisteil sauber abschneiden.

Go dirname.go
package main

import (
	"fmt"
	"strings"
)

func dirname(p string) string {
	i := strings.LastIndexAny(p, `/\`)
	if i < 0 {
		return "."
	}
	return p[:i]
}

func main() {
	fmt.Println(dirname("/etc/nginx/nginx.conf"))
	fmt.Println(dirname(`C:\Users\michael\notes.md`))
	fmt.Println(dirname("plain.txt"))
}
Output
/etc/nginx
C:\Users\michael
.

Der Rohstring `/\` enthält genau die beiden Trennzeichen ohne Escape-Aufwand. Fällt der Treffer weg, signalisiert i < 0, dass kein Verzeichnis-Teil existiert, und die Funktion liefert den POSIX-üblichen .-Wert zurück.

Bei Versions-Strings wie app-1.2.3 oder lib.2024-11 ist offen, ob das letzte semantische Trennzeichen ein . oder ein - ist. LastIndexAny ermittelt den jeweils rechten Trenner und teilt den String an genau dieser Stelle.

Go suffix.go
package main

import (
	"fmt"
	"strings"
)

func splitSuffix(s string) (head, tail string) {
	i := strings.LastIndexAny(s, ".-")
	if i < 0 {
		return s, ""
	}
	return s[:i], s[i+1:]
}

func main() {
	for _, v := range []string{"app-1.2.3", "lib.2024-11", "kernel", "v1.0-rc1"} {
		h, t := splitSuffix(v)
		fmt.Printf("%-12s -> head=%q tail=%q\n", v, h, t)
	}
}
Output
app-1.2.3    -> head="app-1.2" tail="3"
lib.2024-11  -> head="lib.2024" tail="11"
kernel       -> head="kernel" tail=""
v1.0-rc1     -> head="v1.0" tail="rc1"

Beide Trennzeichen liegen gleichberechtigt im Cutset; allein die Position im String entscheidet, welches gewinnt. Soll dagegen . Vorrang vor - haben, müsste man zuerst LastIndex(s, ".") versuchen und nur bei Fehlschlag auf - ausweichen — LastIndexAny selbst kennt keine Priorität innerhalb seines Cutsets.

Cutset ist rune-aware

chars wird als Menge von Unicode-Codepoints gelesen, nicht als Bytemenge — Multi-Byte-Runen funktionieren ohne Sondercode.

Rückgabe ist ein Byte-Offset

Der int zeigt auf ein Byte in s, nicht auf einen Rune-Index; bei UTF-8-Sequenzen auf das erste Byte der Rune.

-1 bei Nicht-Treffer

Findet sich keine Rune aus chars in s, ist das Resultat -1 — ein einziger Check deckt alle Fehlpfade ab.

Leerer Cutset ergibt -1

LastIndexAny(s, "") matcht nichts und liefert immer -1, unabhängig von s.

Leeres s ergibt -1

Ohne Inhalt gibt es nichts zu durchsuchen — der Aufruf endet sofort mit -1.

Threadsafe auf Strings

Strings sind in Go immutabel; parallele Aufrufe auf demselben s sind ohne Sperren erlaubt.

Ersetzt mehrere LastIndex-Calls

Ein LastIndexAny-Aufruf mit Cutset spart die Schleife aus mehreren LastIndex-Aufrufen plus Maximum-Berechnung.

Schneller als regex

Für reine Zeichenmengen ohne Quantoren ist LastIndexAny deutlich günstiger als regexp — keine Engine-Initialisierung, kein Backtracking.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Das strings-Paket — String-Manipulation

Zur Übersicht