ZetCode

Golang Regexp.FindAllStringSubmatchIndex

最后修改于 2025 年 4 月 20 日

本教程将介绍如何在 Go 中使用 Regexp.FindAllStringSubmatchIndex 方法。我们将涵盖其功能并提供实际示例。

一个 正则表达式 是一个定义搜索模式的字符序列。它用于在字符串中进行模式匹配。

Regexp.FindAllStringSubmatchIndex 方法返回一个索引切片,用于标识正则表达式的最左匹配项。它同时提供匹配项和子匹配项的位置。

基本的 FindAllStringSubmatchIndex 示例

此示例展示了 FindAllStringSubmatchIndex 的最简单用法。我们在字符串中查找简单模式的所有匹配项。

basic_find.go
package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`\d+`)
    str := "I have 3 apples and 5 oranges"
    
    indices := re.FindAllStringSubmatchIndex(str, -1)
    fmt.Println(indices)
    
    for _, match := range indices {
        fmt.Println("Match:", str[match[0]:match[1]])
    }
}

该方法返回一个整数切片的切片。每个内部切片包含成对的索引,表示匹配位置。这里我们查找所有数字序列。

提取子匹配项索引

此示例演示了如何获取匹配项和子匹配项的索引。我们将日期字符串解析为各个部分。

submatch_indices.go
package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
    str := "2025-04-20 and 2026-05-21 are important dates"
    
    indices := re.FindAllStringSubmatchIndex(str, -1)
    
    for _, match := range indices {
        fmt.Println("Full match:", str[match[0]:match[1]])
        fmt.Println("Year:", str[match[2]:match[3]])
        fmt.Println("Month:", str[match[4]:match[5]])
        fmt.Println("Day:", str[match[6]:match[7]])
    }
}

索引在开始和结束位置之间交替。整个匹配项位于 0-1,随后是成对的子匹配项。这使我们能够提取匹配项的特定部分。

查找重叠匹配项

此示例展示了如何在字符串中查找重叠匹配项。我们查找所有可能的 3 个字母的序列。

overlapping.go
package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`(?=(\w{3}))`)
    str := "Golang"
    
    indices := re.FindAllStringSubmatchIndex(str, -1)
    
    for _, match := range indices {
        fmt.Println(str[match[2]:match[3]])
    }
}

前瞻断言 (?=...) 允许查找重叠匹配项。每个匹配项显示输入字符串中不同的 3 个字符序列。

提取键值对

这里我们从字符串解析键值对,并使用索引提取它们。这对于配置解析很有用。

key_value.go
package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`(\w+)=("[^"]*"|\S+)`)
    str := `name="John Doe" age=30 city="New York"`
    
    indices := re.FindAllStringSubmatchIndex(str, -1)
    
    for _, match := range indices {
        key := str[match[2]:match[3]]
        value := str[match[4]:match[5]]
        fmt.Printf("Key: %s, Value: %s\n", key, value)
    }
}

该模式匹配键(单词字符)和值(带引号的字符串或非空格序列)。索引有助于精确提取这些部分。

查找 HTML 标签属性

此示例演示了提取 HTML 标签属性及其确切位置。我们解析一个锚标签的 href 和 title。

html_attrs.go
package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`Link`
    
    indices := re.FindAllStringSubmatchIndex(str, -1)
    
    if len(indices) > 0 {
        match := indices[0]
        fmt.Println("Full tag:", str[match[0]:match[1]])
        fmt.Println("Href:", str[match[2]:match[3]])
        fmt.Println("Title:", str[match[4]:match[5]])
    }
}

该正则表达式匹配一个带有 href 和 title 属性的锚标签。索引使我们能够提取这些属性及其在原始字符串中的确切位置。

处理多个匹配项

此示例展示了在更长的文本中处理多个匹配项。我们查找所有电子邮件地址及其位置。

multiple_matches.go
package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})`)
    str := `Contact us at info@example.com or support@company.co.uk for help.`
    
    indices := re.FindAllStringSubmatchIndex(str, -1)
    
    for i, match := range indices {
        fmt.Printf("Match %d:\n", i+1)
        fmt.Println("  Full email:", str[match[0]:match[1]])
        fmt.Println("  Username:", str[match[2]:match[3]])
        fmt.Println("  Domain:", str[match[4]:match[5]])
        fmt.Println("  Position:", match[0], "-", match[1])
    }
}

该方法查找文本中的所有电子邮件地址。对于每个电子邮件地址,我们获取完整的电子邮件、用户名、域及其在原始字符串中的确切位置。

带有命名捕获组的复杂模式

这个高级示例使用命名捕获组并处理结果。我们解析带有时间戳和严重性级别的日志条目。

named_groups.go
package main

import (
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile(`(?P<time>\d{2}:\d{2}:\d{2}) (?P<level>\w+): (?P<message>.*)`)
    str := `10:30:45 ERROR: File not found
11:15:22 WARNING: Disk space low
12:01:33 INFO: Backup completed`
    
    indices := re.FindAllStringSubmatchIndex(str, -1)
    names := re.SubexpNames()
    
    for _, match := range indices {
        for i, name := range names {
            if i != 0 && name != "" {
                start := match[2*i]
                end := match[2*i+1]
                fmt.Printf("%s: %s\n", name, str[start:end])
            }
        }
        fmt.Println()
    }
}

命名捕获组使代码更具可读性。我们使用 SubexpNames 将组索引映射到名称,然后使用匹配索引提取值。

来源

Go regexp 包文档

本教程通过模式匹配和带位置信息的文本处理的实际示例,介绍了 Go 中的 Regexp.FindAllStringSubmatchIndex 方法。

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Go 教程