Skip to content

正则表达式的高级用法处理更复杂的文本模式:断言、非捕获组、命名捕获、贪婪控制、动态构建。

1. 先行断言和后行断言 ​

注意:Go 标准库 regexp 使用 RE2 引擎,不支持前瞻((?=...)、(?!...))和后行断言((?<=...)、(?<!...)),使用这类语法会直接报错。下面先介绍这两个概念(在 PCRE/Python 等引擎中可用),再给出在 Go 中的等价写法。

1.1 先行断言(Lookahead)

  • 定义:先行断言用于检查某个模式是否在另一个模式之前出现,但不会包括在匹配结果中。
  • 语法:(?=pattern) 表示前瞻断言,(?!pattern) 表示否定前瞻断言。
  • Go 中的等价写法:把"前面的模式 + 后面的模式"作为一个整体匹配,再取出需要的捕获组。
  • 示例:查找所有跟在数字后面的字母(不包括数字本身)。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        // 等价于 \d(?=[a-zA-Z]):数字与字母一起匹配,再取出数字部分
        re := regexp.MustCompile(`(\d)([a-zA-Z])`)
        matches := re.FindAllStringSubmatch("123abc 456def 789", -1)
        var digits []string
        for _, m := range matches {
            digits = append(digits, m[1])
        }
        fmt.Println(digits) // Output: [3 6]
    }

1.2 后行断言(Lookbehind)

  • 定义:后行断言用于检查某个模式是否在另一个模式之后出现,但不会包括在匹配结果中。
  • 语法:(?<=pattern) 表示正向后瞻断言,(?<!pattern) 表示否定后瞻断言。
  • Go 中的等价写法:把"前面的模式 + 要取出的模式"作为一个整体匹配,再取出后半部分捕获组。
  • 示例:查找所有前面跟有“abc”的字母。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        // 等价于 (?<=abc)[a-zA-Z]:abc 与字母一起匹配,再取出字母部分
        re := regexp.MustCompile(`abc([a-zA-Z])`)
        matches := re.FindAllStringSubmatch("abcD abcE fgh", -1)
        var letters []string
        for _, m := range matches {
            letters = append(letters, m[1])
        }
        fmt.Println(letters) // Output: [D E]
    }

2. 非捕获组 ​

2.1 非捕获组(Non-Capturing Groups)

  • 定义:非捕获组用于分组,但不会捕获匹配的内容。
  • 语法:(?:pattern)
  • 示例:匹配一系列的数字,但不捕获分组。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        re := regexp.MustCompile(`(?:\d{3}-\d{2}-\d{4})`)
        matches := re.FindAllString("123-45-6789 987-65-4321", -1)
        fmt.Println(matches) // Output: [123-45-6789 987-65-4321]
    }

3. 命名捕获组 ​

3.1 命名捕获组

  • 定义:命名捕获组允许你为捕获的组指定一个名字,便于在代码中引用。
  • 语法:(?P<name>pattern)
  • 示例:提取日期的年、月、日,并为它们命名。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        re := regexp.MustCompile(`(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})`)
        match := re.FindStringSubmatch("2024-08-02")
        if len(match) > 0 {
            fmt.Println("Year:", match[re.SubexpIndex("year")])   // Output: Year: 2024
            fmt.Println("Month:", match[re.SubexpIndex("month")]) // Output: Month: 08
            fmt.Println("Day:", match[re.SubexpIndex("day")])     // Output: Day: 02
        }
    }

4. 贪婪与非贪婪匹配 ​

4.1 贪婪匹配(Greedy Matching)

  • 定义:贪婪匹配尽可能多地匹配字符。
  • 示例:匹配一个或多个字母,包括可能的多个匹配项。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        re := regexp.MustCompile(`[a-z]+`)
        matches := re.FindAllString("abc 123 def ghi", -1)
        fmt.Println(matches) // Output: [abc def ghi]
    }

4.2 非贪婪匹配(Non-Greedy Matching)

  • 定义:非贪婪匹配尽可能少地匹配字符。
  • 语法:在量词后面加上 ?(如 *?, +?, ??)
  • 示例:匹配最短的字母序列。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        re := regexp.MustCompile(`<.*?>`)
        matches := re.FindAllString("<a> <b> <c>", -1)
        fmt.Println(matches) // Output: [<a> <b> <c>]
    }

5. 捕获和引用 ​

5.1 捕获组

  • 定义:捕获组用于提取匹配的子串,并可以在替换操作中引用。
  • 语法:(pattern)
  • 示例:将匹配的子串反转。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        re := regexp.MustCompile(`(\d{3})-(\d{2})-(\d{4})`)
        result := re.ReplaceAllString("123-45-6789", "$3-$2-$1")
        fmt.Println(result) // Output: 6789-45-123
    }

5.2 组的引用

  • 定义:在替换字符串中引用捕获组。
  • 语法:$n(n 是组的编号)
  • 示例:将匹配的数字组逆序。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        re := regexp.MustCompile(`(\d+)-(\d+)`)
        result := re.ReplaceAllString("123-456 789-012", "$2-$1")
        fmt.Println(result) // Output: 456-123 012-789
    }

6. 动态正则表达式 ​

6.1 动态构建正则表达式

  • 定义:根据运行时数据动态生成正则表达式。
  • 示例:动态创建用于匹配用户提供的关键字。
    go
    package main
    
    import (
        "fmt"
        "regexp"
    )
    
    func main() {
        keyword := "example"
        re := regexp.MustCompile(fmt.Sprintf(`\b%s\b`, keyword))
        matches := re.FindAllString("This is an example of dynamic regex.", -1)
        fmt.Println(matches) // Output: [example]
    }

这些语法里,Go regexp 支持非捕获组、命名捕获组、贪婪/非贪婪量词、捕获引用和动态构建;先行/后行断言不支持,得按第 1 节的方式改写成等价模式。

Hello Golang