Golang Regexp.FindStringSubmatchIndex
最后修改于 2025 年 4 月 20 日
本教程将介绍如何在 Go 中使用 `Regexp.FindStringSubmatchIndex` 方法。我们将涵盖其功能并提供实际示例。
一个 正则表达式 是一个定义搜索模式的字符序列。它用于在字符串中进行模式匹配。
`Regexp.FindStringSubmatchIndex` 方法返回一个切片,其中包含识别最左边匹配及其子匹配的索引对。
基本的 FindStringSubmatchIndex 示例
`FindStringSubmatchIndex` 最简单的用法是查找匹配项及其位置。在这里,我们定位一个简单的单词及其位置。
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]]) } }
该方法返回一个切片,其中 `[0]` 是匹配的起始索引,`[1]` 是结束索引。我们可以使用它们来提取匹配的子字符串。
查找子匹配位置
此示例展示了如何获取带括号的子表达式的位置。我们将解析日期字符串并获取组件的位置。
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]]) } }
索引在起始和结束位置之间交替。完整匹配在 `[0:1]`,第一个子匹配在 `[2:3]`,第二个在 `[4:5]`,依此类推。
提取多个匹配项
此示例演示了如何在字符串中查找所有匹配项及其位置。我们将定位所有以 '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` 返回一个索引切片。每个内部切片包含一个匹配项的位置。
命名捕获组
此示例展示了如何处理命名捕获组及其位置。我们将把 URL 解析成组件。
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]) } } } }
命名组可以通过 `SubexpNames` 访问。索引遵循相同的模式,但可以映射到名称以获得清晰度。
验证和提取数据
此示例在提取组件的同时验证字符串格式。我们将检查电话号码格式并获取其部分。
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") } }
该方法具有双重目的:通过非 nil 返回值进行验证,并通过位置索引进行数据提取。
复杂模式匹配
此示例演示了具有嵌套组的更复杂模式。我们将解析带有时间戳和严重性级别的日志条目。
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]) } } }
该模式使用命名组以获得清晰度。索引数组随着模式中每个附加捕获组的增加而增长。
性能注意事项
当性能很重要时,请考虑重用编译的模式和结果。此示例对不同方法进行了基准测试。
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)) }
基准测试显示重用编译的模式可以带来显著的性能提升。尽可能优先选择预编译。
来源
本教程通过实际的模式匹配和位置提取示例,介绍了 Go 中的 `Regexp.FindStringSubmatchIndex` 方法。