Golang Regexp.SubexpIndex
最后修改于 2025 年 4 月 20 日
本教程将解释如何在 Go 中使用 Regexp.SubexpIndex 方法。我们将涵盖命名捕获组并提供实际示例。
正则表达式中的 命名捕获组 允许为匹配的子模式分配名称。这使得模式更具可读性和可维护性。
Regexp.SubexpIndex 方法返回具有给定名称的第一个子表达式的索引。如果不存在子表达式,则返回 -1。
基本 SubexpIndex 示例
SubexpIndex 最简单的用法是获取命名组的索引。在这里,我们使用命名组提取日期组件。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
date := "2025-04-20"
matches := re.FindStringSubmatch(date)
if matches != nil {
yearIdx := re.SubexpIndex("year")
monthIdx := re.SubexpIndex("month")
dayIdx := re.SubexpIndex("day")
fmt.Println("Year:", matches[yearIdx])
fmt.Println("Month:", matches[monthIdx])
fmt.Println("Day:", matches[dayIdx])
}
}
我们为年、月、日定义了命名组。SubexpIndex 获取它们在匹配结果中的位置。这使得代码更具可读性。
检查组是否存在
SubexpIndex 对不存在的组返回 -1。此示例显示了如何安全地检查组是否存在。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<name>\w+)\s+(?P<age>\d+)`)
input := "John 30"
matches := re.FindStringSubmatch(input)
if matches != nil {
nameIdx := re.SubexpIndex("name")
ageIdx := re.SubexpIndex("age")
emailIdx := re.SubexpIndex("email") // Doesn't exist
if nameIdx != -1 {
fmt.Println("Name:", matches[nameIdx])
}
if ageIdx != -1 {
fmt.Println("Age:", matches[ageIdx])
}
if emailIdx != -1 {
fmt.Println("Email:", matches[emailIdx])
} else {
fmt.Println("Email field not found")
}
}
}
在访问每个组之前,我们会检查它是否存在。“email”组返回 -1,因为它在模式中未定义。
与 FindAll Submatches 一起使用
SubexpIndex 与 FindAllStringSubmatch 一起用于处理多个匹配。在这里,我们解析多个日志条目。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`\[(?P<time>\d{2}:\d{2}:\d{2})\] (?P<level>\w+): (?P<message>.*)`)
logs := []string{
"[10:23:45] ERROR: File not found",
"[10:23:47] INFO: User logged in",
"[10:23:49] WARNING: Disk space low",
}
timeIdx := re.SubexpIndex("time")
levelIdx := re.SubexpIndex("level")
msgIdx := re.SubexpIndex("message")
for _, log := range logs {
matches := re.FindStringSubmatch(log)
if matches != nil {
fmt.Printf("Time: %s, Level: %s, Message: %s\n",
matches[timeIdx], matches[levelIdx], matches[msgIdx])
}
}
}
该模式从每个日志条目中提取时间戳、日志级别和消息。与数字索引相比,命名组使代码更清晰。
动态组访问
SubexpIndex 支持基于运行时值的动态组访问。此示例显示了处理不同的字段名。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<first>\w+)\s+(?P<last>\w+),\s+(?P<age>\d+)`)
input := "Smith John, 42"
fields := []string{"first", "last", "age"}
matches := re.FindStringSubmatch(input)
if matches != nil {
for _, field := range fields {
idx := re.SubexpIndex(field)
if idx != -1 {
fmt.Printf("%s: %s\n", field, matches[idx])
}
}
}
}
我们遍历所需的字段名并动态获取它们的值。当处理不同的模式或配置时,这种方法效果很好。
与 SubexpNames 结合使用
SubexpIndex 可以与 SubexpNames 结合使用来处理所有命名组。在这里,我们打印所有组名和值。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<protocol>https?)://(?P<domain>[^/]+)/(?P<path>.*)`)
url := "https://example.com/path/to/resource"
matches := re.FindStringSubmatch(url)
if matches != nil {
for _, name := range re.SubexpNames() {
if name != "" { // Skip unnamed groups
idx := re.SubexpIndex(name)
fmt.Printf("%s: %s\n", name, matches[idx])
}
}
}
}
SubexpNames 返回所有组名,包括未命名组的空字符串。我们过滤掉这些,并打印每个命名组的值。
使用无效模式进行错误处理
在使用 Compile (而不是 MustCompile) 时,我们需要处理命名组模式中可能出现的错误。
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := `(?P<name>\w+)(?P<invalid`
re, err := regexp.Compile(pattern)
if err != nil {
fmt.Println("Error compiling regex:", err)
return
}
// This would panic if we used MustCompile
idx := re.SubexpIndex("name")
fmt.Println("Name group index:", idx)
}
无效的模式(缺少闭括号)会导致 MustCompile 恐慌。Compile 允许我们优雅地处理错误。
带有嵌套组的复杂模式
SubexpIndex 可以与包含嵌套组的复杂模式一起使用。此示例解析了一个配置行。
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<key>\w+)\s*=\s*(?P<value>"(?P<quoted>[^"]*)"|(?P<unquoted>\S+))`)
config := `title = "Welcome" timeout=30`
keyIdx := re.SubexpIndex("key")
valueIdx := re.SubexpIndex("value")
quotedIdx := re.SubexpIndex("quoted")
unquotedIdx := re.SubexpIndex("unquoted")
matches := re.FindAllStringSubmatch(config, -1)
for _, match := range matches {
fmt.Println("Key:", match[keyIdx])
fmt.Println("Value:", match[valueIdx])
if match[quotedIdx] != "" {
fmt.Println("(Quoted value)")
} else {
fmt.Println("(Unquoted value)")
}
}
}
该模式同时处理带引号和不带引号的值。SubexpIndex 有助于清晰地访问此复杂匹配结构中的特定部分。
来源
本教程通过实际的正则表达式命名捕获组使用示例,涵盖了 Go 中的 Regexp.SubexpIndex 方法。