Finding a substring
Go strings have no methods, so searching is done with functions from the strings package. strings.Index(s, sub) returns the byte index where sub first appears in s, or -1 if it doesn't appear at all. The index is the position of the first character of the match.
sentence := "Hello World"
idx := strings.Index(sentence, "World")
fmt.Println(idx) // 6
if strings.Index(sentence, "xyz") == -1 {
fmt.Println("not found")
}
In "Hello World", the letters of Hello take indexes 0 to 4, the space is at index 5, and World starts at index 6. A Go file that calls strings.Index needs import "strings".
Exercise step by step
- Start from the given variable.
sentencealready holds"Hello World". - Declare
idxwith:=. This creates a new variable and gives it the type of the value on the right, which isint. - Call
strings.Index(sentence, "World"). The string to search comes first and the text to look for comes second. Mixing up the order searches for the wrong thing.
The solution in Go
sentence := "Hello World"
idx := strings.Index(sentence, "World")
After this line, idx is 6.
Related functions
The same package has a few more search functions that you will meet often:
strings.LastIndex(s, sub)returns the index of the last occurrence.strings.LastIndex("go gopher", "go")is 3, whilestrings.Indexon the same text gives 0.strings.Contains(s, sub)returns abool. Use it when you only need to know whether the text is there, not where.strings.Index(s[start:], sub)searches from a starting position. The returned index is relative to the slice, so addstartto get the position in the original string.
sentence := "Hello World"
fmt.Println(strings.Index(sentence[3:], "o")) // 1
The slice sentence[3:] is "lo World", where the first o is at index 1. In the original string it is at index 4, which is 3 + 1.
Gotchas
- Test for -1, not for zero. Index 0 is a valid result, meaning the text starts the string. Writing
if idxdoesn't compile anyway, because Go has no truthiness for numbers. Compare with-1explicitly. - Only the first match.
strings.Indexstops at the first occurrence. To find every match, loop and search again from after the previous one. - Case matters.
strings.Index("Hello World", "world")is -1, becausewandWdiffer. Convert both strings withstrings.ToLowerfirst for a case-insensitive search. - Byte positions. The index counts bytes, not characters. In
"héllo",strings.Index("héllo", "l")is 3, becauseétakes two bytes. For ASCII text the two counts agree. - The empty string. Searching for
""returns 0, since an empty string matches at the start of any string. - An unused variable is an error. If you declare
idxand never use it in a full program, Go refuses to compile. Print it or use it.
Finding every occurrence
strings.Index reports only the first match, but you can find all of them by searching again from just after each hit. Because the index of a search on a slice is relative to that slice, add the offset back each time:
text := "banana"
start := 0
for {
i := strings.Index(text[start:], "an")
if i == -1 {
break
}
fmt.Println(start + i)
start += i + 1
}
This prints the positions of the two matches:
1
3
The loop moves start one place past each match, so overlapping matches are found too. Go has no while keyword; a for with no condition runs until break. If you only need the number of matches, strings.Count(text, "an") counts non-overlapping ones directly.
When you only need a yes or no
If the position doesn't matter, a boolean function reads better than comparing an index with -1. strings.Contains(s, sub) says whether the text appears anywhere. strings.HasPrefix(s, "Hello") and strings.HasSuffix(s, "World") check only the start or the end of the string, and both are true for "Hello World". Reach for Index when the position itself is what you need, for example to slice the text before or after a match.
Key takeaways
strings.Index(s, sub)returns the byte index of the first match, or -1.strings.LastIndexfinds the last match, andstrings.Containsanswers yes or no.- Always compare the result with
-1before using it as an index.