Skip to content

Latest commit

 

History

59 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

str logo

Fluent string helpers for Go.

Go Reference License: MIT Go Test Go 1.24 or newer Latest tag Coverage Tests

str wraps a Go string so cleanup and transformation steps can be chained from left to right. Standard-named operations follow Go's strings contracts, including byte indexes, empty searches, and iterators. Application helpers such as Slice, Take, and padding remain rune-based.

Installation

Requires Go 1.24 or newer. Version 3 changes existing signatures and behavior; see the v2 to v3 migration guide.

go get github.com/goforj/str/v3

Quick start

package main

import (
	"fmt"

	"github.com/goforj/str/v3"
)

func main() {
	result := str.Of("  welcome_to_go  ").TrimSpace().Headline().String()
	fmt.Println(result) // Welcome to Go
}

API principles

str keeps the API deliberately small. These rules decide what belongs:

  • Chains come first. Start with str.Of or str.Join. Methods that change text return a new str.String, so the chain can continue. Checks, counts, parsers, and splits return ordinary Go values.
  • One job, one name. There are no aliases or compatibility shims. If two names mean the same thing, keep the clearer one.
  • Preserve Go's contracts. Standard-named operations retain argument order and behavior. The source string becomes the receiver and a single string result becomes String; multiple results, slices, and iterators retain their standard types. Join(elements, sep) starts a chain.
  • Keep units explicit. Index, LastIndex, and the other standard index operations return byte offsets. Application helpers such as Slice, Take, and padding use runes. Never pass a byte index directly to a rune-based helper.
  • Preserve edge cases. Standard searches match empty strings, empty replacement searches insert at UTF-8 boundaries, and negative Repeat counts panic. Parsers and pattern operations return errors as documented.
  • Keep the scope clear. The Go 1.27 string-function surface is available on Go 1.24, including a CutLast backport. Stateful strings.Builder, strings.Reader, and strings.Replacer remain in the standard library. Additional helpers solve common application problems.
  • Examples must keep working. Every public operation has a generated example, and the test suite runs each one and checks its output.

Why not just the standard library?

Often, you should. Go's strings, unicode, strconv, and regexp packages are the right choice when you only need one or two operations. This is already clear:

username := strings.ToLower(strings.TrimSpace("  GoForj_Admin  "))
// goforj_admin

The same cleanup with str reads from left to right:

username := str.Of("  GoForj_Admin  ").TrimSpace().ToLower().String()
// goforj_admin

Either version is reasonable. The difference is easier to see when more rules belong together.

Using the standard library:

func configKey(name string) string {
	key := strings.TrimSpace(name)
	key = strings.ToUpper(key)
	key = strings.ReplaceAll(key, "-", "_")
	key = strings.Trim(key, "_")
	if !strings.HasPrefix(key, "APP_") {
		key = "APP_" + key
	}
	return key
}

// configKey("  --billing-worker--  ") == "APP_BILLING_WORKER"

Using str:

func configKey(name string) string {
	return str.Of(name).
		TrimSpace().
		ToUpper().
		ReplaceAll("-", "_").
		Trim("_").
		EnsurePrefix("APP_").
		String()
}

// configKey("  --billing-worker--  ") == "APP_BILLING_WORKER"

Some jobs do not have a single standard library call. For example, Slug handles case, punctuation, repeated separators, and Unicode letters. It can be one step in a longer chain that turns a report title into a CSV filename with a 64-rune base name:

func exportFilename(reportTitle string) string {
	return str.Of(reportTitle).
		ReplaceAll("&", "and").
		Slug().
		Take(64).
		Trim("-").
		EnsurePrefix("report-").
		EnsureSuffix(".csv").
		String()
}

filename := exportFilename("Q3 Sales & Returns - North America")
// report-q3-sales-and-returns-north-america.csv

str uses the standard library underneath and has no dependencies. Use whichever version makes the rules easiest to see.

Performance

These comparisons measure equivalent standard-library and str operations. Each cell reports the median of 10 samples as ns/op · B/op · allocs/op.

Recorded with go1.27.0 on linux/arm64 using -cpu=1 (GOMAXPROCS=1).

Workload Standard library str chain
TrimSpace 3.2 ns/op · 0 B/op · 0 allocs/op 3.2 ns/op · 0 B/op · 0 allocs/op
ToLower 64.4 ns/op · 32 B/op · 1 allocs/op 65.6 ns/op · 32 B/op · 1 allocs/op
NormalizeSpace (Fields + Join) 208.8 ns/op · 208 B/op · 2 allocs/op 188.1 ns/op · 80 B/op · 1 allocs/op
TrimSpace → ToLower 66.9 ns/op · 32 B/op · 1 allocs/op 69.2 ns/op · 32 B/op · 1 allocs/op
ReplaceAll × 3 131.6 ns/op · 96 B/op · 3 allocs/op 144.1 ns/op · 96 B/op · 3 allocs/op

Timing is machine-specific; use it to understand the scale of these operations, not as a universal speed claim. Treat small timing differences within the raw sample spread as noise. Allocation counts are less sensitive to machine speed and show how much heap work each composition performs. In these workloads, wrapping and unwrapping added no heap allocations; allocations came from transformations that produced new text. NormalizeSpace is algorithmically different: the standard-library composition builds a field slice before joining it, while str uses one builder pass.

The benchmark source and committed raw output record exactly what ran, including the Go version and command. Refresh the measurements explicitly with go -C docs run ./readme -record-benchmarks; ordinary README generation only renders that frozen snapshot.

API index

The full API and these examples are also available on pkg.go.dev.

Group API
Affixes EnsurePrefix · EnsureSuffix · TrimPrefix · TrimSuffix · Unwrap · Wrap
Case Camel · Headline · Kebab · LcFirst · Pascal · Snake · Title · ToLower · ToLowerSpecial · ToTitle · ToTitleSpecial · ToUpper · ToUpperSpecial · UcFirst
Checks IsASCII · IsAlnum · IsAlpha · IsBlank · IsEmpty · IsNumeric
Cleanup Deduplicate · NormalizeNewlines · NormalizeSpace · Trim · TrimFunc · TrimLeft · TrimLeftFunc · TrimRight · TrimRightFunc · TrimSpace
Comparison EqualFold
Compose Append · Prepend
Constructor Of
Conversion Bool · Float64 · Int
Encoding FromBase64 · ToBase64
Fluent GoString · String
Length RuneCount
Masking Mask
Match Match
Padding PadBoth · PadLeft · PadRight
Pluralize Plural · Singular
Replace Remove · Replace · ReplaceAll · ReplaceArray · ReplaceFirst · ReplaceFold · ReplaceLast · ReplacePrefix · ReplaceSuffix · Swap
Search Compare · Contains · ContainsAny · ContainsFold · ContainsFunc · ContainsRune · Count · HasPrefix · HasPrefixFold · HasSuffix · HasSuffixFold · Index · IndexAny · IndexByte · IndexFunc · IndexRune · LastIndex · LastIndexAny · LastIndexByte · LastIndexFunc
Slug Slug
Snippet Excerpt
Split Fields · FieldsFunc · FieldsFuncSeq · FieldsSeq · Lines · Split · SplitAfter · SplitAfterN · SplitAfterSeq · SplitN · SplitSeq
Substrings After · AfterLast · Before · BeforeLast · Between · CharAt · CommonPrefix · CommonSuffix · Cut · CutLast · CutPrefix · CutSuffix · Limit · Slice · SubstrReplace · Take · TakeLast
Transform Clone · Map · Repeat · Reverse · ToValidUTF8
Words FirstWord · Initials · Join · LastWord · SplitWords · WordCount · Words · WrapWords

API examples

These examples come from GoDoc and run as part of the test suite.

Affixes

EnsurePrefix

EnsurePrefix ensures the string starts with prefix, adding it if missing. Similar: EnsureSuffix and TrimPrefix.

v := str.Of("path/to").EnsurePrefix("/").String()
println(v)
// #string /path/to

EnsureSuffix

EnsureSuffix ensures the string ends with suffix, adding it if missing. Similar: EnsurePrefix and TrimSuffix.

v := str.Of("path/to").EnsureSuffix("/").String()
println(v)
// #string path/to/

TrimPrefix

TrimPrefix removes prefix when it appears at the start of the string. Similar: TrimSuffix and EnsurePrefix.

v := str.Of("https://goforj.dev").TrimPrefix("https://").String()
println(v)
// #string goforj.dev

TrimSuffix

TrimSuffix removes suffix when it appears at the end of the string. Similar: TrimPrefix and EnsureSuffix.

v := str.Of("file.txt").TrimSuffix(".txt").String()
println(v)
// #string file

Unwrap

Unwrap removes matching before and after strings if present. Similar: Wrap.

v := str.Of(`"GoForj"`).Unwrap(`"`, `"`).String()
println(v)
// #string GoForj

Wrap

Wrap surrounds the string with before and after. Similar: Unwrap.

v := str.Of("GoForj").Wrap(`"`, `"`).String()
println(v)
// #string "GoForj"

Case

Camel

Camel converts the string to camelCase. Similar: Pascal.

v := str.Of("foo_bar baz").Camel().String()
println(v)
// #string fooBarBaz

Headline

Headline converts the string into a human-friendly headline: splits on case/underscores/dashes/whitespace, title-cases words, and lowercases small words (except the first). Similar: Title.

v := str.Of("emailNotification_sent").Headline().String()
println(v)
// #string Email Notification Sent

Kebab

Kebab converts the string to kebab-case. Similar: Snake.

v := str.Of("fooBar baz").Kebab().String()
println(v)
// #string foo-bar-baz

LcFirst

LcFirst returns the string with the first rune lower-cased. Similar: UcFirst and ToLower.

v := str.Of("Gopher").LcFirst().String()
fmt.Println(v)
// #string gopher

Pascal

Pascal converts the string to PascalCase. Similar: Camel.

v := str.Of("foo_bar baz").Pascal().String()
fmt.Println(v)
// #string FooBarBaz

Snake

Snake converts the string to snake_case. Similar: Kebab.

v := str.Of("fooBar baz").Snake().String()
println(v)
// #string foo_bar_baz

Title

Title title-cases word-initial letters using strings.Title, preserving other letters.

Deprecated: Like strings.Title, its word boundaries do not handle Unicode punctuation properly. Use golang.org/x/text/cases for linguistic title casing.

v := str.Of("hello WORLD").Title().String()
println(v)
// #string Hello WORLD

ToLower

ToLower returns a lowercase copy of the string using Unicode rules. Similar: ToUpper and LcFirst.

v := str.Of("GoLang").ToLower().String()
println(v)
// #string golang

ToLowerSpecial

ToLowerSpecial maps every rune to lowercase using c's language-specific case rules.

v := str.Of("I").ToLowerSpecial(unicode.TurkishCase).String()
println(v)
// #string ı

ToTitle

ToTitle maps every rune to Unicode titlecase.

v := str.Of("go").ToTitle().String()
println(v)
// #string GO

ToTitleSpecial

ToTitleSpecial maps every rune to Unicode titlecase using c's language-specific case rules.

v := str.Of("i").ToTitleSpecial(unicode.TurkishCase).String()
println(v)
// #string İ

ToUpper

ToUpper returns an uppercase copy of the string using Unicode rules. Similar: ToLower and UcFirst.

v := str.Of("GoLang").ToUpper().String()
println(v)
// #string GOLANG

ToUpperSpecial

ToUpperSpecial maps every rune to uppercase using c's language-specific case rules.

v := str.Of("i").ToUpperSpecial(unicode.TurkishCase).String()
println(v)
// #string İ

UcFirst

UcFirst returns the string with the first rune upper-cased. Similar: LcFirst and ToUpper.

v := str.Of("gopher").UcFirst().String()
println(v)
// #string Gopher

Checks

IsASCII

IsASCII reports whether the string consists solely of 7-bit ASCII runes.

v := str.Of("gopher").IsASCII()
println(v)
// #bool true

IsAlnum

IsAlnum reports whether the string contains at least one rune and every rune is a Unicode letter or number.

v := str.Of("Gopher2025").IsAlnum()
println(v)
// #bool true

IsAlpha

IsAlpha reports whether the string contains at least one rune and every rune is a Unicode letter.

v := str.Of("Gopher").IsAlpha()
println(v)
// #bool true

IsBlank

IsBlank reports whether the string contains only Unicode whitespace. Similar: IsEmpty.

v := str.Of("  \t\n")
println(v.IsBlank())
// #bool true

IsEmpty

IsEmpty reports whether the string has zero length. Similar: IsBlank.

v := str.Of("").IsEmpty()
println(v)
// #bool true

IsNumeric

IsNumeric reports whether the string contains at least one rune and every rune is a Unicode number.

v := str.Of("12345").IsNumeric()
println(v)
// #bool true

Cleanup

Deduplicate

Deduplicate collapses consecutive instances of char into a single instance. If char is zero, space is used. Similar: NormalizeSpace.

v := str.Of("The   Go   Playground").Deduplicate(' ').String()
println(v)
// #string The Go Playground

NormalizeNewlines

NormalizeNewlines replaces CRLF, CR, and Unicode separators with \n. Similar: Lines.

v := str.Of("a\r\nb\u2028c").NormalizeNewlines().String()
println(v)
// #string a\nb\nc

NormalizeSpace

NormalizeSpace removes surrounding whitespace and collapses internal whitespace to single spaces. Similar: TrimSpace.

v := str.Of("  go   forj  ").NormalizeSpace().String()
println(v)
// #string go forj

Trim

Trim removes leading and trailing runes contained in cutset.

v := str.Of("..GoForj!!").Trim(".!").String()
println(v)
// #string GoForj

TrimFunc

TrimFunc removes leading and trailing runes satisfying f.

v := str.Of("12Go34").TrimFunc(unicode.IsDigit).String()
println(v)
// #string Go

TrimLeft

TrimLeft removes leading runes contained in cutset.

v := str.Of("..GoForj!!").TrimLeft(".!").String()
println(v)
// #string GoForj!!

TrimLeftFunc

TrimLeftFunc removes leading runes satisfying f.

v := str.Of("12Go34").TrimLeftFunc(unicode.IsDigit).String()
println(v)
// #string Go34

TrimRight

TrimRight removes trailing runes contained in cutset.

v := str.Of("..GoForj!!").TrimRight(".!").String()
println(v)
// #string ..GoForj

TrimRightFunc

TrimRightFunc removes trailing runes satisfying f.

v := str.Of("12Go34").TrimRightFunc(unicode.IsDigit).String()
println(v)
// #string 12Go

TrimSpace

TrimSpace removes leading and trailing Unicode whitespace.

v := str.Of("  GoForj  ").TrimSpace().String()
println(v)
// #string GoForj

Comparison

EqualFold

EqualFold reports whether the string matches other using Unicode simple case folding.

v := str.Of("gopher").EqualFold("GOPHER")
println(v)
// #bool true

Compose

Append

Append concatenates the provided parts to the end of the string. Similar: Prepend.

v := str.Of("Go").Append("Forj", "!").String()
println(v)
// #string GoForj!

Prepend

Prepend concatenates the provided parts to the beginning of the string. Similar: Append.

v := str.Of("World").Prepend("Hello ", "Go ").String()
println(v)
// #string Hello Go World

Constructor

Of

Of wraps a raw string with fluent helpers.

v := str.Of("gopher")
println(v.String())
// #string gopher

Conversion

Bool

Bool parses the string as a bool using strconv.ParseBool semantics. Similar: Int and Float64.

v, err := str.Of("true").Bool()
println(v, err == nil)
// #bool true
// #bool true

Float64

Float64 parses the string as a float64 using strconv.ParseFloat semantics. Similar: Bool and Int.

v, err := str.Of("3.14").Float64()
fmt.Println(v, err == nil)
// #float64 3.14
// #bool true

Int

Int parses the string as a base-10 int using strconv.Atoi semantics. Similar: Bool and Float64.

v, err := str.Of("42").Int()
println(v, err == nil)
// #int 42
// #bool true

Encoding

FromBase64

FromBase64 decodes a standard Base64 string. Similar: ToBase64.

v, err := str.Of("Z29waGVy").FromBase64()
println(v.String(), err == nil)
// #string gopher
// #bool true

ToBase64

ToBase64 encodes the string using standard Base64. Similar: FromBase64.

v := str.Of("gopher").ToBase64().String()
println(v)
// #string Z29waGVy

Fluent

GoString

GoString allows %#v formatting to print the raw string.

v := str.Of("go")
println(fmt.Sprintf("%#v", v))
// #string go

String

String returns the underlying raw string value.

v := str.Of("go").String()
println(v)
// #string go

Length

RuneCount

RuneCount returns the number of Unicode code points in the string.

v := str.Of("gophers 🦫").RuneCount()
println(v)
// #int 9

Masking

Mask

Mask replaces the middle of the string with the given rune, revealing revealLeft runes at the start and revealRight runes at the end. Negative reveal values count from the end. If the reveal counts cover the whole string, the original string is returned.

v := str.Of("gopher@example.com").Mask('*', 3, 4).String()
println(v)
// #string gop***********.com

Match

Match

Match reports whether the entire string matches pattern using [path.Match] syntax. A malformed pattern returns an error, and wildcards do not match a slash.

matched, err := str.Of("billing:reports").Match("billing:*")
println(matched, err == nil)
// #bool true
// #bool true

Padding

PadBoth

PadBoth pads the string on both sides to reach length runes using pad (defaults to space). Widths at or below the current rune width leave the string unchanged. Similar: PadLeft and PadRight.

v := str.Of("go").PadBoth(6, "-").String()
println(v)
// #string --go--

PadLeft

PadLeft pads the string on the left to reach length runes using pad (defaults to space). Widths at or below the current rune width leave the string unchanged. Similar: PadRight and PadBoth.

v := str.Of("go").PadLeft(5, " ").String()
println(v)
// #string \u0020\u0020\u0020go

PadRight

PadRight pads the string on the right to reach length runes using pad (defaults to space). Widths at or below the current rune width leave the string unchanged. Similar: PadLeft and PadBoth.

v := str.Of("go").PadRight(5, ".").String()
println(v)
// #string go...

Pluralize

Plural

Plural returns a best-effort English plural form of the final identifier word. It handles common English forms and identifier boundaries without claiming to resolve every irregular or ambiguous noun. Similar: Singular.

v := str.Of("city").Plural().String()
println(v)
// #string cities

Singular

Singular returns a best-effort English singular form of the final identifier word. It handles common English forms and identifier boundaries without claiming to resolve every irregular or ambiguous noun. Similar: Plural.

v := str.Of("people").Singular().String()
println(v)
// #string person

Replace

Remove

Remove deletes all occurrences of provided substrings.

v := str.Of("The Go Toolkit").Remove("Go ").String()
println(v)
// #string The Toolkit

Replace

Replace replaces the first n non-overlapping occurrences of old with new. A negative n replaces all matches; zero leaves the string unchanged. An empty old matches at the beginning and after each UTF-8 sequence.

v := str.Of("go go go").Replace("go", "Go", 2).String()
println(v)
// #string Go Go go

ReplaceAll

ReplaceAll replaces all non-overlapping occurrences of old with new. An empty old matches at the beginning and after each UTF-8 sequence.

v := str.Of("go gopher go").ReplaceAll("go", "Go").String()
println(v)
// #string Go Gopher Go

ReplaceArray

ReplaceArray replaces all occurrences of each old in olds with repl. Entries are applied sequentially, including replacements produced by earlier entries. Empty entries insert repl at UTF-8 boundaries, like ReplaceAll. Similar: ReplaceAll and Swap.

v := str.Of("The---Go---Toolkit")
println(v.ReplaceArray([]string{"---"}, "-").String())
// #string The-Go-Toolkit

ReplaceFirst

ReplaceFirst replaces the first occurrence of old with repl. An empty old inserts repl at the beginning. Similar: ReplaceLast and ReplaceAll.

v := str.Of("gopher gopher").ReplaceFirst("gopher", "go").String()
println(v)
// #string go gopher

ReplaceFold

ReplaceFold replaces all non-overlapping occurrences of old with repl using Unicode simple case folding. An empty old inserts repl at UTF-8 boundaries, like ReplaceAll. Similar: ReplaceAll.

v := str.Of("go gopher GO").ReplaceFold("GO", "Go").String()
println(v)
// #string Go Gopher Go

ReplaceLast

ReplaceLast replaces the last occurrence of old with repl. An empty old inserts repl at the end. Similar: ReplaceFirst and ReplaceAll.

v := str.Of("gopher gopher").ReplaceLast("gopher", "go").String()
println(v)
// #string gopher go

ReplacePrefix

ReplacePrefix replaces old with repl when old is a prefix of the string. An empty old inserts repl at the beginning. Similar: ReplaceSuffix and TrimPrefix.

v := str.Of("prefix-value").ReplacePrefix("prefix-", "new-").String()
println(v)
// #string new-value

ReplaceSuffix

ReplaceSuffix replaces old with repl when old is a suffix of the string. An empty old inserts repl at the end. Similar: ReplacePrefix and TrimSuffix.

v := str.Of("file.old").ReplaceSuffix(".old", ".new").String()
println(v)
// #string file.new

Swap

Swap replaces multiple values in one pass using strings.Replacer built from a map. Longer keys take priority at the same position; replacements are not rescanned. Empty keys follow strings.Replacer byte boundaries and can split a multibyte UTF-8 rune. Use ReplaceAll for empty-search insertion at UTF-8 sequence boundaries. Similar: ReplaceArray.

pairs := map[string]string{"Gophers": "GoForj", "are": "is", "great": "fantastic"}
v := str.Of("Gophers are great!").Swap(pairs).String()
println(v)
// #string GoForj is fantastic!

Search

Compare

Compare returns -1, 0, or 1 according to lexicographic byte order.

v := str.Of("go").Compare("rust")
println(v)
// #int -1

Contains

Contains reports whether the string contains sub using a case-sensitive comparison. An empty substring always matches. Similar: ContainsFold.

v := str.Of("Go means gophers").Contains("gopher")
println(v)
// #bool true

ContainsAny

ContainsAny reports whether any rune in chars occurs in the string.

v := str.Of("gopher").ContainsAny("aeiou")
println(v)
// #bool true

ContainsFold

ContainsFold reports whether the string contains sub using Unicode simple case folding. An empty substring always matches. Similar: Contains.

v := str.Of("Go means gophers").ContainsFold("GOPHER")
println(v)
// #bool true

ContainsFunc

ContainsFunc reports whether any rune satisfies f.

v := str.Of("go2").ContainsFunc(unicode.IsDigit)
println(v)
// #bool true

ContainsRune

ContainsRune reports whether r occurs in the string.

v := str.Of("café").ContainsRune('é')
println(v)
// #bool true

Count

Count counts non-overlapping occurrences of sub. An empty sub matches at the beginning and after each UTF-8 sequence.

v := str.Of("gogophergo").Count("go")
println(v)
// #int 3

HasPrefix

HasPrefix reports whether the string starts with prefix using a case-sensitive comparison. An empty prefix always matches. Similar: HasPrefixFold and HasSuffix.

v := str.Of("gopher").HasPrefix("go")
println(v)
// #bool true

HasPrefixFold

HasPrefixFold reports whether the string starts with prefix using Unicode simple case folding. An empty prefix always matches. Similar: HasPrefix and HasSuffixFold.

v := str.Of("gopher").HasPrefixFold("GO")
println(v)
// #bool true

HasSuffix

HasSuffix reports whether the string ends with suffix using a case-sensitive comparison. An empty suffix always matches. Similar: HasSuffixFold and HasPrefix.

v := str.Of("gopher").HasSuffix("her")
println(v)
// #bool true

HasSuffixFold

HasSuffixFold reports whether the string ends with suffix using Unicode simple case folding. An empty suffix always matches. Similar: HasSuffix and HasPrefixFold.

v := str.Of("gopher").HasSuffixFold("HER")
println(v)
// #bool true

Index

Index returns the byte index of the first occurrence of sub, or -1 if not found. Similar: LastIndex.

v := str.Of("héllo").Index("llo")
println(v)
// #int 3

IndexAny

IndexAny returns the byte offset of the first rune in chars, or -1 if absent.

v := str.Of("go").IndexAny("o")
println(v)
// #int 1

IndexByte

IndexByte returns the byte offset of the first byte equal to c, or -1 if absent.

v := str.Of("go").IndexByte('o')
println(v)
// #int 1

IndexFunc

IndexFunc returns the byte offset of the first rune satisfying f, or -1 if absent.

v := str.Of("go2").IndexFunc(unicode.IsDigit)
println(v)
// #int 2

IndexRune

IndexRune returns the byte offset of the first rune equal to r, or -1 if absent.

v := str.Of("go").IndexRune('o')
println(v)
// #int 1

LastIndex

LastIndex returns the byte index of the last occurrence of sub, or -1 if not found. Similar: Index.

v := str.Of("go gophers go").LastIndex("go")
println(v)
// #int 11

LastIndexAny

LastIndexAny returns the byte offset of the last rune in chars, or -1 if absent.

v := str.Of("go").LastIndexAny("o")
println(v)
// #int 1

LastIndexByte

LastIndexByte returns the byte offset of the last byte equal to c, or -1 if absent.

v := str.Of("go").LastIndexByte('o')
println(v)
// #int 1

LastIndexFunc

LastIndexFunc returns the byte offset of the last rune satisfying f, or -1 if absent.

v := str.Of("go2").LastIndexFunc(unicode.IsDigit)
println(v)
// #int 2

Slug

Slug

Slug returns a lowercase Unicode slug separated by hyphens. Unicode letters and digits are preserved, while every other run is collapsed to one hyphen. Similar: Kebab.

v := str.Of("Go Forj Toolkit").Slug().String()
println(v)
// #string go-forj-toolkit

Snippet

Excerpt

Excerpt returns a snippet around the first occurrence of needle with the given radius. If needle is not found, an empty string is returned. If radius <= 0, a default of 100 is used. Omission is used at the start/end when text is trimmed (default "...").

v := str.Of("This is my name").Excerpt("my", 3, "...")
println(v.String())
// #string ...is my na...

Split

Fields

Fields splits the string into fields separated by Unicode whitespace. Consecutive separators are combined; empty or separator-only input yields no fields.

v := str.Of("a b c").Fields()
fmt.Println(v)
// #[]string [a b c]

FieldsFunc

FieldsFunc splits the string into fields separated by runes satisfying f. The predicate must return the same result for a given rune; its call order is unspecified. Consecutive separators are combined; empty or separator-only input yields no fields.

v := str.Of("a b c").FieldsFunc(unicode.IsSpace)
fmt.Println(v)
// #[]string [a b c]

FieldsFuncSeq

FieldsFuncSeq returns an iterator over fields separated by runes satisfying f. Each iteration starts again from the beginning of the string. The predicate must return the same result for a given rune; its call order is unspecified. Consecutive separators are combined; empty or separator-only input yields no fields.

v := slices.Collect(str.Of("a b c").FieldsFuncSeq(unicode.IsSpace))
fmt.Println(v)
// #[]string [a b c]

FieldsSeq

FieldsSeq returns an iterator over fields separated by Unicode whitespace. Each iteration starts again from the beginning of the string. Consecutive separators are combined; empty or separator-only input yields no fields.

v := slices.Collect(str.Of("a b c").FieldsSeq())
fmt.Println(v)
// #[]string [a b c]

Lines

Lines returns a single-use iterator over newline-terminated lines. Newline bytes are retained; empty input yields no lines and a trailing newline does not produce an extra empty line. Use NormalizeNewlines().Split("\n") when normalized, delimiter-free fields are wanted.

v := slices.Collect(str.Of("a\nb").Lines())
fmt.Printf("%q\n", v)
// #[]string ["a\\n" "b"]

Split

Split splits the string by the given separator.

v := str.Of("a,b,c").Split(",")
fmt.Println(v)
// #[]string [a b c]

SplitAfter

SplitAfter returns substrings including their trailing separator. An empty sep splits after each UTF-8 sequence.

v := str.Of("a,b,c").SplitAfter(",")
fmt.Println(v)
// #[]string [a, b, c]

SplitAfterN

SplitAfterN returns substrings including their trailing separator. A positive n limits the result to n substrings; zero returns nil and negative n has no limit. An empty sep splits after each UTF-8 sequence.

v := str.Of("a,b,c").SplitAfterN(",", 2)
fmt.Println(v)
// #[]string [a, b,c]

SplitAfterSeq

SplitAfterSeq returns a single-use iterator over substrings including their trailing separator. An empty sep splits after each UTF-8 sequence.

v := slices.Collect(str.Of("a,b,c").SplitAfterSeq(","))
fmt.Println(v)
// #[]string [a, b, c]

SplitN

SplitN returns substrings separated by sep. A positive n limits the result to n substrings; zero returns nil and negative n has no limit. An empty sep splits after each UTF-8 sequence.

v := str.Of("a,b,c").SplitN(",", 2)
fmt.Println(v)
// #[]string [a b,c]

SplitSeq

SplitSeq returns a single-use iterator over substrings separated by sep. An empty sep splits after each UTF-8 sequence.

v := slices.Collect(str.Of("a,b,c").SplitSeq(","))
fmt.Println(v)
// #[]string [a b c]

Substrings

After

After returns the substring after the first occurrence of sep. If sep is empty or not found, the original string is returned. Similar: AfterLast and Before.

v := str.Of("gopher::go").After("::").String()
println(v)
// #string go

AfterLast

AfterLast returns the substring after the last occurrence of sep. If sep is empty or not found, the original string is returned. Similar: After and BeforeLast.

v := str.Of("pkg/path/file.txt").AfterLast("/").String()
println(v)
// #string file.txt

Before

Before returns the substring before the first occurrence of sep. If sep is empty or not found, the original string is returned. Similar: BeforeLast and After.

v := str.Of("gopher::go").Before("::").String()
println(v)
// #string gopher

BeforeLast

BeforeLast returns the substring before the last occurrence of sep. If sep is empty or not found, the original string is returned. Similar: Before and AfterLast.

v := str.Of("pkg/path/file.txt").BeforeLast("/").String()
println(v)
// #string pkg/path

Between

Between returns the substring between the first start marker and the first end marker after it. It returns an empty string when either marker is empty or missing.

v := str.Of("[first] and [second]").Between("[", "]").String()
println(v)
// #string first

CharAt

CharAt returns the rune at the given index and true if within bounds. Similar: Slice and RuneCount.

v, ok := str.Of("gopher").CharAt(2)
println(string(v), ok)
// #string p
// #bool true

CommonPrefix

CommonPrefix returns the longest shared prefix between the string and all provided others. Comparison is rune-safe. If no others are provided, the original string is returned. Similar: CommonSuffix.

v := str.Of("gopher").CommonPrefix("go", "gold").String()
println(v)
// #string go

CommonSuffix

CommonSuffix returns the longest shared suffix between the string and all provided others. Comparison is rune-safe. If no others are provided, the original string is returned. Similar: CommonPrefix.

v := str.Of("main_test.go").CommonSuffix("user_test.go", "api_test.go").String()
println(v)
// #string _test.go

Cut

Cut splits around the first occurrence of sep. An empty sep is a match. On no match it returns the original string, "", false.

before, after, found := str.Of("go:forj").Cut(":")
fmt.Println(before, after, found)
// #string go forj true

CutLast

CutLast splits around the last occurrence of sep. An empty sep is a match. On no match it returns the original string, "", false. CutLast preserves the Go 1.27 contract without raising the Go 1.24 minimum.

before, after, found := str.Of("go:forj").CutLast(":")
fmt.Println(before, after, found)
// #string go forj true

CutPrefix

CutPrefix removes prefix and reports whether it was present. An empty prefix is a match.

value, found := str.Of("go:forj").CutPrefix("go:")
fmt.Println(value, found)
// #string forj true

CutSuffix

CutSuffix removes suffix and reports whether it was present. An empty suffix is a match.

value, found := str.Of("go:forj").CutSuffix(":forj")
fmt.Println(value, found)
// #string go true

Limit

Limit truncates the string to length runes, appending suffix if truncation occurs.

v := str.Of("Perfectly balanced, as all things should be.").Limit(10, "...").String()
println(v)
// #string Perfectly\u0020...

Slice

Slice returns the substring between rune offsets [start:end). Index and LastIndex return bytes, which must not be passed directly here. Indices are clamped; if start >= end the result is empty.

v := str.Of("naïve café").Slice(3, 7).String()
println(v)
// #string ve c

SubstrReplace

SubstrReplace replaces the rune slice in [start:end) with repl.

v := str.Of("naïve café").SubstrReplace("i", 2, 3).String()
println(v)
// #string naive café

Take

Take returns the first length runes of the string (clamped). Similar: TakeLast and Limit.

v := str.Of("gophers").Take(3).String()
println(v)
// #string gop

TakeLast

TakeLast returns the last length runes of the string (clamped). Similar: Take.

v := str.Of("gophers").TakeLast(4).String()
println(v)
// #string hers

Transform

Clone

Clone copies the underlying bytes into a fresh allocation. Use it to release a large backing string retained by a small substring. Empty input returns an empty string without allocating.

v := str.Of("gopher").Clone().String()
println(v)
// #string gopher

Map

Map maps each rune using mapping, dropping runes mapped to a negative value.

v := str.Of("go").Map(unicode.ToUpper).String()
println(v)
// #string GO

Repeat

Repeat repeats the string count times. It panics if count is negative or the result length overflows int.

v := str.Of("go").Repeat(3).String()
println(v)
// #string gogogo

Reverse

Reverse returns a rune-safe reversed string.

v := str.Of("naïve").Reverse().String()
println(v)
// #string evïan

ToValidUTF8

ToValidUTF8 replaces each run of invalid UTF-8 bytes with replacement.

v := str.Of("a\xff\xfeb").ToValidUTF8("?").String()
println(v)
// #string a?b

Words

FirstWord

FirstWord returns the first detected word or an empty string. Similar: LastWord and SplitWords.

v := str.Of("Hello world")
println(v.FirstWord().String())
// #string Hello

Initials

Initials returns the uppercase first rune of each detected word. Words are split the same way as SplitWords, including camel case and acronym boundaries. Similar: SplitWords.

v := str.Of("portableNetwork graphics").Initials().String()
println(v)
// #string PNG

Join

Join concatenates elements with sep and returns the result to the fluent chain. Join starts a chain from a slice without discarding an existing receiver. Similar: Split.

v := str.Join([]string{"foo", "bar"}, "-").String()
println(v)
// #string foo-bar

LastWord

LastWord returns the last detected word or an empty string. Similar: FirstWord and SplitWords.

v := str.Of("Hello world").LastWord().String()
println(v)
// #string world

SplitWords

SplitWords splits the string into Unicode words, including camel case and acronym boundaries. Similar: FirstWord, LastWord, WordCount, and Words.

v := str.Of("one, two, three").SplitWords()
fmt.Println(v)
// #[]string [one two three]

WordCount

WordCount returns the number of detected words. Similar: SplitWords.

v := str.Of("Hello, world!").WordCount()
println(v)
// #int 2

Words

Words limits the string to count words, preserving the source through the selected word boundary and appending suffix if truncated. Similar: SplitWords and WrapWords.

v := str.Of("Perfectly balanced, as all things should be.").Words(3, " >>>").String()
println(v)
// #string Perfectly balanced, as >>>

WrapWords

WrapWords wraps the string to the given rune width on whitespace boundaries, using breakStr between lines without discarding punctuation. Similar: Words.

v := str.Of("The quick brown fox jumped over the lazy dog.").WrapWords(20, "\n").String()
println(v)
// #string The quick brown fox\njumped over the lazy\ndog.

Documentation

Development

docs and examples are separate Go modules, keeping their tooling and generated programs out of the library module download.

Use make test, make test-race, make vet, and make generate. The test and vet targets cover all three modules; generation rebuilds the examples and README.

Licensed under the MIT License.

Releases

Packages

Used by

Contributors

Languages