Published on 11/9/2023

Lesser Known Functions in Go's strings Package

When it comes to string manipulation in Go, the strings package is the go-to choice for many developers. It offers a wide range of functions for common tasks like string concatenation, trimming, and replacing. However, beyond the well-known functions like strings.Join or strings.Replace, there exists a treasure trove of lesser-known string functions that can simplify how you go about manipulation strings in Go.

Such functions save you time and effort in handling strings in various scenarios, from text processing and sorting to user input validation and tokenization.

strings.Clone

func Clone(s string) string

The Clone function takes a string s and returns a new string with the same contents. This is used when you need to ensure that you have a copy of a string that is independent of the original string, which can be important to avoid unexpected side effects in your program when strings are changed.

Use cases for strings.Clone typically involve situations where strings are passed around and modified across different scopes or goroutines. Cloning a string ensures that modifications in one part of the program do not affect the original string elsewhere.

package main

import (
	"fmt"
	"strings"
)

func main() {
	// Original string
	greeting := "Hello, World!"

	// Using strings.Clone to create a copy
	clonedGreeting := strings.Clone(greeting)

	// Modifying the original string
	greeting = "Hi, Universe!"

	// Printing both the original and the cloned string
	fmt.Println("Original:", greeting)       // Output: Original: Hi, Universe!
	fmt.Println("Cloned:", clonedGreeting)   // Output: Cloned: Hello, World!
}

strings.Compare

func Compare(a, b string) int

The Compare function takes two strings a and b and returns an integer comparing the two strings lexicographically. The result will be:

  • 0 if a == b,
  • -1 if a < b, and
  • +1 if a > b.

strings.Compare is often used in sorting algorithms where strings need to be ordered alphabetically. It can also be used in conditional statements to branch logic depending on the lexicographic order of two strings.

package main

import (
	"fmt"
	"sort"
	"strings"
)

// ByAlpha implements sort.Interface for []string based on
// lexicographical order using strings.Compare.
type ByAlpha []string

func (a ByAlpha) Len() int           { return len(a) }
func (a ByAlpha) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAlpha) Less(i, j int) bool { return strings.Compare(a[i], a[j]) < 0 }

func main() {
	// Unsorted slice of strings
	fruits := []string{"peach", "banana", "apple"}

	// Sorting the slice
	sort.Sort(ByAlpha(fruits))

	fmt.Println("Sorted fruits:", fruits) // Output: Sorted fruits: [apple banana peach]
}

strings.Count

func Count(s, substr string) int

The Count function takes a string s and a substring substr and returns the number of non-overlapping instances of substr in s.

This function is commonly used in text processing where you need to find out how many times a particular word or phrase appears in a body of text. It can be used in analytics, such as counting keywords for SEO or finding the frequency of certain terms in user input.

package main

import (
	"fmt"
	"strings"
)

func main() {
	// A sample text
	text := "As easy as A, B, C, A, B, C"

	// Counting the number of occurrences of the substring "A"
	countA := strings.Count(text, "A")
	fmt.Println("Number of 'A':", countA) // Output: Number of 'A': 3

	// Counting the number of occurrences of the substring "B, C"
	countBC := strings.Count(text, "B, C")
	fmt.Println("Number of 'B, C':", countBC) // Output: Number of 'B, C': 2
}

strings.Cut

func Cut(s, sep string) (before, after string, found bool)

The Cut function takes a string s and a separator sep and attempts to split s into two parts: before and after the first occurrence of sep. It also returns a boolean found that indicates whether the separator was found in the string.

strings.Cut is useful for parsing strings where you need to extract parts of the string separated by a known delimiter. It is simpler and more efficient than using regular expressions for simple tokenization tasks, such as extracting the username from an email address.

package main

import (
	"fmt"
	"strings"
)

func main() {
	// A sample email address
	email := "[email protected]"

	// Using strings.Cut to separate the username from the domain
	username, domain, found := strings.Cut(email, "@")

	// Check if the separator was found and print the result
	if found {
		fmt.Println("Username:", username) // Output: Username: username
		fmt.Println("Domain:", domain)     // Output: Domain: example.com
	} else {
		fmt.Println("The separator was not found in the string.")
	}
}

strings.EqualFold

func EqualFold(s, t string) bool

The EqualFold function takes two strings s and t and reports whether s and t, interpreted as UTF-8 strings, are equal under Unicode case-folding, which is a more general form of case-insensitivity.

Use strings.EqualFold when comparing strings for equality in a case-insensitive manner. This is especially useful in user input validation to ensure that variations in case do not affect the logic of the program, such as when comparing email addresses or usernames.

package main

import (
	"fmt"
	"strings"
)

func main() {
	// Two strings to compare, differing in case
	str1 := "GoLang"
	str2 := "golang"

	// Using strings.EqualFold to compare the strings case-insensitively
	equal := strings.EqualFold(str1, str2)

	// Printing the result
	fmt.Println("Are str1 and str2 equal (case-insensitive)?", equal) // Output: Are str1 and str2 equal (case-insensitive)? true
}

strings.Fields

func Fields(s string) []string

The Fields function takes a string s and returns a slice of substrings of s, separated by one or more whitespace characters.

strings.Fields is used when you need to break up a string into words or tokens. This can be used to parse user commands, analyze text, or any situation where space-delimited processing is needed.

package main

import (
	"fmt"
	"strings"
)

func main() {
	// A string with multiple fields separated by whitespace
	sentence := "The quick brown fox jumps over the lazy dog"

	// Using strings.Fields to break the string into words
	words := strings.Fields(sentence)

	// Printing the words
	fmt.Println("Words:", words) // Output: Words: [The quick brown fox jumps over the lazy dog]
}

strings.IndexAny

func IndexAny(s, chars string) int

The IndexAny function takes a string s and a string chars and returns the index of the first occurrence of any Unicode code point from chars in s, or -1 if no Unicode code point from chars is present.

strings.IndexAny is particularly useful when you want to find the first occurrence of any character from a set within a string. This can be applied in parsing tasks, such as finding the next special character in a CSV file or identifying invalid input in a form field.

package main

import (
	"fmt"
	"strings"
)

func main() {
	// A sample string
	s := "foo(bar);"

	// Characters to find in the string
	chars := "()"

	// Using strings.IndexAny to find the index of the first occurrence
	// of any character in the set "()"
	index := strings.IndexAny(s, chars)

	// Printing the index
	fmt.Println("Index of first occurrence of any character '()':", index) // Output: Index of first occurrence of any character '()': 3
}

If you require more advanced text manipulation in Go, you will love the golang.org/x/text package, which provides a rich set of functions for working with text, more specifically text processing and Unicode (e.g., casing, searching, encoding).