Golang Regexp.FindStringSubmatchIndex
last modified April 20, 2025
This tutorial explains how to use the Regexp.FindStringSubmatchIndex
method in Go.
We'll cover its functionality and provide practical examples.
A regular expression is a sequence of characters that defines a search pattern. It's used for pattern matching within strings.
The Regexp.FindStringSubmatchIndex method returns a slice holding the index pairs identifying the leftmost match and its submatches.
Basic FindStringSubmatchIndex Example
The simplest use of FindStringSubmatchIndex
finds matches and their
positions. Here we locate a simple word and its position.
package main import ( "fmt" "regexp" ) func main() { re := regexp.MustCompile(`hello`) str := "hello world" indices := re.FindStringSubmatchIndex(str) fmt.Println(indices) // [0 5] if indices != nil { fmt.Println("Match found at:", indices[0], "to", indices[1]) fmt.Println("Matched text:", str[indices[0]:indices[1]]) } }
The method returns a slice where [0] is the start index and [1] is the end index of the match. We can use these to extract the matched substring.
Finding Submatch Positions
This example shows how to get positions for parenthesized subexpressions. We'll parse a date string and get component positions.
package main import ( "fmt" "regexp" ) func main() { re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`) str := "Today is 2025-04-20" indices := re.FindStringSubmatchIndex(str) if indices != nil { fmt.Println("Full match:", str[indices[0]:indices[1]]) fmt.Println("Year:", str[indices[2]:indices[3]]) fmt.Println("Month:", str[indices[4]:indices[5]]) fmt.Println("Day:", str[indices[6]:indices[7]]) } }
The indices alternate between start and end positions. The full match is at [0:1], first submatch at [2:3], second at [4:5], etc.
Extracting Multiple Matches
This example demonstrates finding all matches and their positions in a string. We'll locate all words starting with 'h'.
package main import ( "fmt" "regexp" ) func main() { re := regexp.MustCompile(`\bh\w+`) str := "hello world, hi there, how are you?" allIndices := re.FindAllStringSubmatchIndex(str, -1) for _, indices := range allIndices { word := str[indices[0]:indices[1]] fmt.Printf("Found '%s' at %d-%d\n", word, indices[0], indices[1]) } }
FindAllStringSubmatchIndex
returns a slice of index slices. Each
inner slice contains positions for one match.
Named Capture Groups
This example shows how to work with named capture groups and their positions. We'll parse a URL into components.
package main import ( "fmt" "regexp" ) func main() { re := regexp.MustCompile(`(?P<protocol>https?)://(?P<host>[^/]+)(?P<path>/.*)?`) str := "https://example.com/path/to/resource" indices := re.FindStringSubmatchIndex(str) if indices != nil { for i, name := range re.SubexpNames() { if i != 0 && name != "" { start, end := indices[2*i], indices[2*i+1] fmt.Printf("%s: %s\n", name, str[start:end]) } } } }
Named groups are accessed via SubexpNames
. The indices follow the
same pattern but can be mapped to names for clarity.
Validating and Extracting Data
This example validates a string format while extracting components. We'll check a phone number format and get its parts.
package main import ( "fmt" "regexp" ) func main() { re := regexp.MustCompile(`^(\d{3})-(\d{3})-(\d{4})$`) phone := "123-456-7890" indices := re.FindStringSubmatchIndex(phone) if indices != nil { fmt.Println("Valid phone number") fmt.Println("Area code:", phone[indices[2]:indices[3]]) fmt.Println("Exchange:", phone[indices[4]:indices[5]]) fmt.Println("Subscriber:", phone[indices[6]:indices[7]]) } else { fmt.Println("Invalid phone number format") } }
The method serves dual purpose: validation through non-nil return and data extraction through the position indices.
Complex Pattern Matching
This example demonstrates a more complex pattern with nested groups. We'll parse log entries with timestamps and severity levels.
package main import ( "fmt" "regexp" ) func main() { re := regexp.MustCompile(`^\[(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (?P<level>\w+): (?P<message>.*)`) log := "[2025-04-20 14:30:00] ERROR: Database connection failed" indices := re.FindStringSubmatchIndex(log) if indices != nil { names := re.SubexpNames() for i := 1; i < len(names); i++ { start, end := indices[2*i], indices[2*i+1] fmt.Printf("%s: %s\n", names[i], log[start:end]) } } }
The pattern uses named groups for clarity. The indices array grows with each additional capturing group in the pattern.
Performance Considerations
When performance matters, consider reusing compiled patterns and results. This example benchmarks different approaches.
package main import ( "fmt" "regexp" "time" ) func main() { str := "sample text with multiple 2025-04-20 dates to 2025-04-21 find" re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`) // Approach 1: Compile and find in loop start := time.Now() for i := 0; i < 1000; i++ { re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`) re.FindStringSubmatchIndex(str) } fmt.Println("Compile in loop:", time.Since(start)) // Approach 2: Precompile and reuse start = time.Now() for i := 0; i < 1000; i++ { re.FindStringSubmatchIndex(str) } fmt.Println("Reuse compiled:", time.Since(start)) }
The benchmark shows significant performance gains from reusing compiled patterns. Always prefer precompiling when possible.
Source
Go regexp package documentation
This tutorial covered the Regexp.FindStringSubmatchIndex
method
in Go with practical examples of pattern matching and position extraction.
Author
List all Go tutorials.