Golang Regexp.FindAllSubmatch
最后修改于 2025 年 4 月 20 日
本教程将介绍如何在 Go 中使用 `Regexp.FindAllSubmatch` 方法。我们将涵盖子匹配项提取基础知识,并提供实际示例。
子匹配项 (submatch) 是正则表达式中由括号括起来的子表达式匹配的字符串的一部分。子匹配项允许提取匹配项的特定部分。
`Regexp.FindAllSubmatch` 方法在字节切片中返回正则表达式的所有连续匹配项,包括所有子匹配项。每个匹配项都是一个字节切片组成的切片。
基本的 FindAllSubmatch 示例
`FindAllSubmatch` 最简单的用法是从字符串中提取所有匹配项和子匹配项。在这里,我们查找文本中的日期组件。
package main import ( "fmt" "regexp" ) func main() { text := "Dates: 2025-04-20, 2025-05-21, 2025-06-22" re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`) matches := re.FindAllSubmatch([]byte(text), -1) for _, match := range matches { fmt.Printf("Full match: %s\n", match[0]) fmt.Printf("Year: %s, Month: %s, Day: %s\n", match[1], match[2], match[3]) } }
该模式在单独的分组中捕获年份、月份和日期。`FindAllSubmatch` 返回所有匹配项及其子匹配项。每个匹配项都是一个切片,其中索引 0 是完整匹配项。
提取键值对
`FindAllSubmatch` 可以从结构化文本中解析键值对。此示例提取配置值。
package main import ( "fmt" "regexp" ) func main() { config := ` host = localhost port = 8080 timeout = 30 debug = true ` re := regexp.MustCompile(`(\w+)\s*=\s*(\S+)`) matches := re.FindAllSubmatch([]byte(config), -1) for _, match := range matches { fmt.Printf("Key: %-10s Value: %s\n", match[1], match[2]) } }
该模式将单词字符作为键,将非空白字符作为值进行匹配。子匹配项分别捕获键和值,以便于处理。
查找 HTML 属性
我们可以使用 `FindAllSubmatch` 从 HTML 标签中提取属性。此示例查找锚点标签中的所有 href 属性。
package main import ( "fmt" "regexp" ) func main() { html := ` <a href="https://example.com">Example</a> <a href="/about">About</a> <a class="nav" href="/contact">Contact</a> ` re := regexp.MustCompile(`<a\b[^>]*\shref="([^"]*)"`) matches := re.FindAllSubmatch([]byte(html), -1) for _, match := range matches { fmt.Printf("Link: %s\n", match[1]) } }
该模式匹配锚点标签并捕获 href 属性值。请注意,对于复杂的 HTML 解析,通常使用专用解析器更好。
解析日志条目
日志文件通常具有可以通过正则表达式解析的统一格式。此示例从 Apache 日志条目中提取组件。
package main import ( "fmt" "regexp" ) func main() { log := `127.0.0.1 - frank [10/Oct/2025:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326` pattern := `^(\S+) (\S+) (\S+) \[([^\]]+)\] "(\S+) (\S+) (\S+)" (\d+) (\d+)$` re := regexp.MustCompile(pattern) matches := re.FindAllSubmatch([]byte(log), -1) if len(matches) > 0 { match := matches[0] fmt.Printf("IP: %s\n", match[1]) fmt.Printf("User: %s\n", match[3]) fmt.Printf("Date: %s\n", match[4]) fmt.Printf("Method: %s\n", match[5]) fmt.Printf("Path: %s\n", match[6]) fmt.Printf("Status: %s\n", match[8]) } }
该模式匹配 Common Log Format 条目的所有组件。每个组件都在单独的子匹配项中捕获,以便于访问。
提取多个电话号码
`FindAllSubmatch` 可以查找文本中模式的所有出现。此示例提取不同格式的电话号码。
package main import ( "fmt" "regexp" ) func main() { text := ` Contact us at 555-123-4567 or (555) 987-6543. International: +1 555 789 0123. ` re := regexp.MustCompile(`(\+?\d{1,3}[-. ]?)?\(?(\d{3})\)?[-. ]?(\d{3})[-. ]?(\d{4})`) matches := re.FindAllSubmatch([]byte(text), -1) for i, match := range matches { fmt.Printf("Phone #%d: %s%s%s%s\n", i+1, match[1], match[2], match[3], match[4]) } }
该模式匹配各种电话号码格式。每个组件(国家代码、区号等)分别捕获,以实现灵活处理。
查找嵌套子匹配项
复杂的模式可能包含嵌套的子匹配项。此示例显示了如何访问不同级别的子匹配项。
package main import ( "fmt" "regexp" ) func main() { text := "Coordinates: (40.7128, -74.0060), (34.0522, -118.2437)" re := regexp.MustCompile(`\((\d+\.\d+),\s*(-?\d+\.\d+)\)`) matches := re.FindAllSubmatch([]byte(text), -1) for i, match := range matches { fmt.Printf("Point %d: Lat=%s, Lon=%s\n", i+1, match[1], match[2]) } }
该模式匹配括号中的纬度-经度对。每个坐标组件都在其自己的子匹配项中捕获,以便单独访问。
限制匹配数量
`FindAllSubmatch` 的第二个参数控制返回的匹配项数量。此示例显示了限制匹配项数量。
package main import ( "fmt" "regexp" ) func main() { text := "apple banana apple cherry apple date apple" re := regexp.MustCompile(`(apple)`) // Find first 2 matches matches := re.FindAllSubmatch([]byte(text), 2) fmt.Printf("Found %d matches:\n", len(matches)) for _, match := range matches { fmt.Println(string(match[0])) } // Find all matches matches = re.FindAllSubmatch([]byte(text), -1) fmt.Printf("\nFound %d total matches\n", len(matches)) }
设置一个正整数会限制返回的匹配项数量。使用 -1 会返回输入字符串中的所有匹配项。
来源
本教程通过从各种文本模式中提取子匹配项的实际示例,介绍了 Go 中的 `Regexp.FindAllSubmatch` 方法。