Before anything else, you need to know that strings are immutable. This means you can't change any character or letter inside a string. If you want to alter it in any way, you can rewrite the whole string. Besides, you can get some information about a string using special helpers (functions) that you can find in the strings package. This topic will teach you how these functions work and when to use them.
Prefix and suffix
How can you check if a string begins with a specific sequence of characters or ends with it? The strings package contains two helpers that can answer this question: HasPrefix and HasSuffix. Both return a boolean value and take two arguments: the string to search in and the substring to search.
HasPrefix begins to search from the left side of the string and returns true if the string begins with the substring we've passed as its argument. HasSuffix, on the contrary, begins to search from the right side and returns true if the string ends with the substring in question.
Both HasPrefix and HasSuffix search for a complete match and are case-sensitive, meaning they distinguish between uppercase and lowercase letters. Take a look at an example of using these functions below:
package main
import(
"fmt"
"strings"
)
func main() {
var source string = "HyperSkill"
var prefix string = "Hype"
hasPrefix := strings.HasPrefix(source, prefix)
hasSuffix := strings.HasSuffix(source, "LL")
fmt.Println(hasPrefix)
fmt.Println(hasSuffix)
}
// Output:
// true
// falseContains
Now, let's imagine you want to find a specific pattern within a string, but you don't know where exactly it is. It can be at the beginning or at the end of the string you have, or it can be placed in the middle of it. All you know is that the pattern should be inside the string. The strings package has a helper that searches for a substring within the whole length of a string. It's called Contains.
Similar to the functions we've discussed in the previous section, Contains returns a boolean value true if the pattern occurs at least once in the string. It also needs a complete match to find the pattern:
package main
import(
"fmt"
"strings"
)
func main() {
var source string = "Test string for Contains"
fmt.Println(strings.Contains(source, "for"))
fmt.Println(strings.Contains(source, "test"))
}
// Output:
// true
// falseRemember that whitespace is also a character, and a string like " for" would not equal to "for".
Index
What if you want not only to know whether a string contains a substring but also the exact place of this substring? The following helpers also search for a substring in a string and return an integer, representing the index of either the start or the end of the first instance of the substring.
The Index helper returns the starting index of the first occurrence of the substring, while LastIndex returns the ending index of the last occurrence of the substring.
Index performs a search from left to right, and LastIndex searches from right to left. Both helpers return a number between zero and the string length in case the string contains the desired substring; otherwise, they return -1. You can see an example of using these functions below:
package main
import (
"fmt"
"strings"
)
func main() {
var source string = "Who is who in Doctor Who?"
fmt.Println(strings.Index(source, "Who"))
fmt.Println(strings.LastIndex(source, "Who"))
fmt.Println(strings.Index(source, "doctor"))
}
// Output:
// 0
// 21
// -1When you work with strings, you often need to know the indexes of all occurrences of a substring. However, the indexing functions return only one number. You can apply knowledge about string slices and loops to solve this problem. The following piece of code prints indexes in the order of pattern appearance:
package main
import (
"fmt"
"strings"
)
func main() {
var source string = "Will Will Smith smith Will Smith?"
i := 0
n := strings.Index(source, "Will")
for i != -1 {
fmt.Println(n)
i = strings.Index(source[n+1:], "Will")
n = n + i + 1
}
}
// Output:
// 0
// 5
// 22Count
Let's return to the function Contains. It tells about a pattern contained in a string, but what if you want to know how many times a substring is repeated in a string? Like in the all-index-search program above, you can devise a way to do it by using decreasing slices and Contains, but the strings package includes a helper that does it for you.
The helper is called Count, and that's precisely what it does: it counts. It takes the source string and the substring and returns the number of instances the substring occurs in the source string:
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Count("Perpetuum mobile", "e"))
fmt.Println(strings.Count("Perpetuum mobile", "z"))
fmt.Println(strings.Count("Perpetuum mobile", ""))
}
// Output:
// 3
// 0
// 17An empty string "" is a special case that deserves a comment. If you use the Count function to give you the number of empty string symbols regardless of the string you use, the function will return a number greater than the string length by one. How can it be? The trick is the beginning of the string: the function will consider the space before the first character as an empty string and will add it to the spaces between characters and the space between the last character and the end of the string: "of" -> "<empty>o<empty>f<empty>".
Case insensitive comparison
All the previous functions use complete matches when searching for a substring. And for example, the capital F for them is not the same thing as an f. The strings package has functions that can convert all symbols in a string to upper or lower case. However, by using these functions, we would modify strings, and in this topic, we talk about features that tell us something about a string but do not change it.
The last helper we'll consider for now is EqualFold, which helps us compare two strings in different cases. EqualFold takes two strings as arguments and returns true if the strings contain the same text and false if they don't. It works as follows:
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.EqualFold("Hello", "hello"))
fmt.Println(strings.EqualFold("Hi!", "hi"))
}
// Output:
// true
// falseConclusion
In this topic, you've learned about functions that help us search for something in strings. Here are the bullet points of what we've covered in the previous sections:
All the helper functions from this topic (except
EqualFold) take the source string and the substring as arguments;EqualFoldtakes two strings to compare;HasPrefix,HasSuffix,Contains, andEqualFoldreturn a boolean value;Index,LastIndex, andCountreturn an integer number.
You will certainly make use of these functions not once when coding in Go, starting with some practice right in the following steps of this topic!