一文带你全面掌握Go语言中的正则表达式

寻技术 Go编程 2023年10月07日 141

正则表达式是一种强大的模式匹配工具,能够在文本中进行灵活的搜索和替换操作。本文将介绍 Golang 中的正则表达式语法,包括常用的匹配符号、模式修饰符以及示例应用。通过深入了解 Golang 正则表达式,您将能够更好地利用这一工具来处理字符串操作。

1. 正则表达式语法

正则表达式是一种用于匹配和操作文本的强大工具,它使用特殊的字符和语法来定义模式。在 Golang 的 regexp 包中,使用的正则表达式语法是基于标准的 POSIX 正则表达式语法的一个子集。

以下是一些常用的正则表达式元字符:

  • . :匹配任意单个字符,除了换行符。
  • * :匹配前面的元素零次或多次。
  • + :匹配前面的元素一次或多次。
  • ? :匹配前面的元素零次或一次。
  • ^ :匹配字符串的开头。
  • $ :匹配字符串的结尾。
  • [] :字符类,匹配方括号中的任意字符。
  • [^] :否定字符类,匹配除方括号中字符以外的任意字符。
  • | :逻辑或,匹配两个模式之一。
  • () :捕获组,用于分组和提取匹配的子字符串。

示例代码:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, World!"
    re := regexp.MustCompile(`H.llo`)
​
    fmt.Println(re.MatchString(str)) // true
}
复制代码

在上面的示例中,我们创建了一个正则表达式对象 re,它使用了.元字符来匹配 H 后面的任意字符,然后是 llo。我们使用 MatchString 方法来检查字符串 str 是否匹配该正则表达式,它将返回 true。

2. 创建正则表达式对象

在 Golang 中,要使用正则表达式进行匹配,首先需要创建一个正则表达式对象。可以使用 regexp.Compile 函数或正则表达式字面量来创建对象。

示例代码:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, World!"
    re := regexp.MustCompile(`[aeiou]`)
​
    fmt.Println(re.MatchString(str)) // true
}
复制代码

当我们运行上述代码时,它将打印出 true,因为正则表达式 [aeiou] 匹配字符串中的任何一个元音字母。

在上面的示例中,我们使用 regexp.MustCompile 函数创建了一个正则表达式对象 re,该函数接受一个字符串参数,表示正则表达式的模式。这个函数会编译正则表达式并返回一个可用于匹配的正则表达式对象。

另外,你还可以使用正则表达式字面量来创建正则表达式对象,如下所示:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, World!"
    re := `[aeiou]`
​
    match, _ := regexp.MatchString(re, str)
    fmt.Println(match) // true
}
复制代码

在这个示例中,我们直接将正则表达式模式作为字符串赋值给变量 re,然后使用 regexp.MatchString 函数检查字符串 str 是否与正则表达式匹配。这个函数返回一个布尔值表示匹配结果。

无论是使用 regexp.MustCompile 函数还是正则表达式字面量,都会创建一个正则表达式对象,该对象可以在后续的匹配操作中使用。

3. 字符串匹配

使用 Golang 的 regexp 包,你可以对字符串进行正则表达式匹配操作。下面是一些常用的方法:

  • MatchString(pattern, s string) bool:检查字符串 s 是否与正则表达式模式 pattern 匹配,返回一个布尔值表示匹配结果。
  • Match(pattern string, b []byte) (matched bool, err error):检查字节切片 b 是否与正则表达式模式 pattern 匹配,返回一个布尔值表示匹配结果。

示例代码:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, World!"
    re := regexp.MustCompile(`^Hello`)
​
    fmt.Println(re.MatchString(str)) // true
}
复制代码

在上面的示例中,我们使用 MatchString 方法检查字符串 str 是否以 Hello 开头。由于 str 的开头确实是 Hello,所以匹配结果为 true。

另外,你也可以使用 Match 方法来检查字节切片是否与正则表达式匹配。例如:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := []byte("Hello, World!")
    re := regexp.MustCompile(`^Hello`)
​
    matched, _ := re.Match(str)
    fmt.Println(matched) // true
}
复制代码

在这个示例中,我们将字符串 str 转换为字节切片,并使用 Match 方法来检查它是否以 Hello 开头。同样,由于匹配成功,所以输出为 true。

通过使用这些方法,你可以轻松地检查字符串是否符合特定的正则表达式模式。

4. 字符串替换

在 Golang 的 regexp 包中,你可以使用正则表达式来进行字符串替换操作。以下是常用的方法:

  • ReplaceAllString(src, repl, pattern string) string:将字符串 src 中所有匹配正则表达式模式 pattern 的部分替换为 repl,并返回替换后的新字符串。
  • ReplaceAllStringFunc(src string, repl func(string) string, pattern string) string:根据匹配的正则表达式模式 pattern,使用 repl 函数对字符串 src 进行替换,并返回替换后的新字符串。repl 函数接收匹配的字符串作为参数,并返回替换后的字符串。

示例代码:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, Golang!"
    re := regexp.MustCompile(`Golang`)
​
    newStr := re.ReplaceAllString(str, "World")
    fmt.Println(newStr) // Hello, World!
}
复制代码

在上面的示例中,我们使用 ReplaceAllString 方法将字符串 str 中的 Golang 替换为 World。替换后的新字符串存储在变量 newStr 中,并打印输出结果为 Hello, World!。

如果你想根据匹配的字符串来动态替换内容,可以使用 ReplaceAllStringFunc 方法。例如:

package main
​
import (
    "fmt"
    "regexp"
    "strings"
)
​
func main() {
    str := "Hello, Golang!"
    re := regexp.MustCompile(`\w+`)
​
    newStr := re.ReplaceAllStringFunc(str, strings.ToUpper)
    fmt.Println(newStr) // HELLO, GOLANG!
}
复制代码

在这个示例中,我们使用 ReplaceAllStringFunc 方法将字符串 str 中的每个单词转换为大写。我们提供了 strings.ToUpper 函数作为替换函数,该函数将匹配的字符串转换为大写形式。结果输出为 HELLO, GOLANG!。

通过这些方法,你可以对字符串进行灵活的替换操作,根据正则表达式模式来实现各种替换需求。

5. 捕获组

在正则表达式中,捕获组是用括号括起来的子表达式,它们允许你在匹配中提取特定的子字符串。Golang 的 regexp 包提供了多个方法来处理捕获组。

  • FindStringSubmatch(s string) []string:返回一个字符串切片,其中包含与正则表达式模式匹配的子字符串及其对应的捕获组。
  • FindAllStringSubmatch(s string, n int) string:返回一个字符串切片的切片,其中包含与正则表达式模式匹配的所有子字符串及其对应的捕获组。可指定 n 来限制匹配的数量。

示例代码:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "John Doe, jane@example.com"
    re := regexp.MustCompile(`(\w+)\s(\w+),\s(\w+@\w+.\w+)`)
​
    match := re.FindStringSubmatch(str)
    fmt.Println(match) // [John Doe, John Doe, jane@example.com]
    fmt.Println("Name:", match[1], match[2])  // Name: John Doe
    fmt.Println("Email:", match[3]) // Email: jane@example.com
}
复制代码

在上面的示例中,我们使用正则表达式 (\w+)\s(\w+),\s(\w+@\w+.\w+) 匹配形如 "John Doe, jane@example.com" 的字符串。该正则表达式包含了三个捕获组,分别用于提取名字、姓氏和电子邮件地址。我们使用 FindStringSubmatch 方法来获取匹配的结果,并通过索引访问捕获组中的子字符串。

当我们打印 match 时,可以看到它是一个字符串切片,其中第一个元素是整个匹配的字符串,后续元素是捕获组中的子字符串。

7. 标志(Flags)

Golang 的 regexp 包还提供了一些标志(flags)选项,用于修改正则表达式的行为。这些标志可以通过在正则表达式模式中添加标志参数来设置。

以下是一些常用的标志:

  • i:忽略大小写,使匹配对大小写不敏感。
  • m:多行模式,允许 ^ 和 $ 匹配文本的每一行的开头和结尾。
  • s:单行模式,使点号 . 可以匹配换行符。
  • U:非贪婪模式,使匹配尽可能少地进行。

示例代码:

package main
​
import (
"fmt"
"regexp"
)
​
func main() {
    str := "Hello\nworld"
    re := regexp.MustCompile((?m)^(\w+))
    matches := re.FindAllStringSubmatch(str, -1)
    for _, match := range matches {
        fmt.Println("Matched:", match[0])
        fmt.Println("Capture Group 1:", match[1])
    }
}
复制代码

在上面的示例中,我们使用正则表达式 (?m)^(\w+) 匹配多行字符串中的每一行的第一个单词。标志 (?m) 启用多行模式,^ 匹配每行的开头,(\w+) 是一个捕获组,用于匹配一个或多个字母数字字符。我们使用 FindAllStringSubmatch 方法来获取所有匹配的结果,并迭代输出每个匹配的字符串和捕获组。

当我们运行该代码时,输出将是:

Matched: Hello
Capture Group 1: Hello
Matched: world
Capture Group 1: world
复制代码

通过设置适当的标志,你可以调整正则表达式的行为,以满足特定的匹配需求。

8. 常见正则表达式技巧

当使用正则表达式时,有一些常见的技巧可以帮助你更有效地处理模式匹配。以下是一些常见的正则表达式技巧。

8.1 使用限定符

限定符用于指定匹配元素的数量。以下是一些常见的限定符:

  • *:匹配前一个元素零次或多次。
  • +:匹配前一个元素一次或多次。
  • ?:匹配前一个元素零次或一次。
  • {n}:匹配前一个元素恰好 n 次。
  • {n,}:匹配前一个元素至少 n 次。
  • {n,m}:匹配前一个元素至少 n 次,但不超过 m 次。
package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "aaaabbbbcccc"
​
    re := regexp.MustCompile(`a{2,}b{2,}c{2,}`)
    match := re.MatchString(str)
    fmt.Println(match) // true
}
复制代码

在上面的示例中,我们使用正则表达式 a{2,}b{2,}c{2,} 匹配连续出现两次或更多次的字母 "a"、"b" 和 "c"。通过使用限定符,我们可以定义所需的匹配次数。

8.2 使用字符类

字符类用于匹配一组特定的字符。以下是一些常见的字符类:

  • [abc]:匹配字符 "a"、"b" 或 "c"。
  • [0-9]:匹配任意一个数字。
  • [^0-9]:匹配除数字以外的任意字符。
package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "a1b2c3"
​
    re := regexp.MustCompile(`[0-9]`)
    matches := re.FindAllString(str, -1)
    fmt.Println(matches) // [1 2 3]
}
复制代码

在上面的示例中,我们使用正则表达式 [0-9] 匹配字符串中的数字字符。通过使用字符类,我们可以定义需要匹配的字符范围。

8.3 使用元字符

元字符具有特殊的含义。以下是一些常见的元字符:

  • .:匹配除换行符以外的任意字符。
  • \w:匹配字母、数字或下划线字符。
  • \d:匹配数字字符。
  • \s:匹配空白字符。
package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, World!"
​
    re := regexp.MustCompile(`\w+`)
    matches := re.FindAllString(str, -1)
    fmt.Println(matches) // [Hello World]
}
复制代码

8.4 使用捕获组

捕获组允许你提取匹配的子字符串。通过使用括号将子表达式括起来,你可以将其作为捕获组。以下是一个示例:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, Golang!"
​
    re := regexp.MustCompile(`(\w+), (\w+)!`)
    matches := re.FindStringSubmatch(str)
    fmt.Println(matches[0])   // Hello, Golang!
    fmt.Println(matches[1])   // Hello
    fmt.Println(matches[2])   // Golang
}
复制代码

在上面的示例中,我们使用正则表达式 (\w+), (\w+)! 匹配以逗号分隔的两个单词,并将它们作为捕获组。通过使用 FindStringSubmatch 方法,我们可以提取整个匹配的子字符串以及每个捕获组的内容。

8.5 使用反向引用

反向引用允许你在正则表达式中引用先前匹配的捕获组。通过使用 \n,其中 n 是捕获组的索引,你可以引用先前的捕获组。以下是一个示例:

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "hello hello"
​
    re := regexp.MustCompile(`(\w+) \1`)
    match := re.MatchString(str)
    fmt.Println(match) // true
}
复制代码

在上面的示例中,我们使用正则表达式 (\w+) \1 匹配重复的单词。\1 表示引用第一个捕获组的内容。通过使用反向引用,我们可以匹配重复出现的模式。

8.6 使用锚点

锚点用于指定匹配发生的位置。以下是一些常见的锚点:

  • ^:匹配字符串的开头。
  • $:匹配字符串的结尾。
  • \b:匹配单词的边界。
package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, Golang!"
​
    re := regexp.MustCompile(`^Hello`)
    match := re.MatchString(str)
    fmt.Println(match) // true
​
    re = regexp.MustCompile(`Golang!$`)
    match = re.MatchString(str)
    fmt.Println(match) // true
​
    re = regexp.MustCompile(`\bGolang\b`)
    match = re.MatchString(str)
    fmt.Println(match) // true
}
复制代码

在上面的示例中,我们使用不同的锚点来匹配字符串的开头、结尾和单词边界。通过使用锚点,我们可以限定匹配发生的位置。

8.7 使用修饰符

修饰符是用于修改正则表达式的行为的特殊标记。它们可以影响匹配的方式和规则。以下是一些常见的修饰符。

8.7.1 i 修饰符(不区分大小写)

使用 i 修饰符可以使匹配过程对大小写不敏感。

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello, World!"
​
    re := regexp.MustCompile(`hello`)
    match := re.MatchString(str)
    fmt.Println(match) // false
​
    re = regexp.MustCompile(`(?i)hello`)
    match = re.MatchString(str)
    fmt.Println(match) // true
}
复制代码

在上面的示例中,正则表达式 (?i)hello 使用了 i 修饰符,使匹配过程不区分大小写。

8.7.2 m 修饰符(多行模式)

使用 m 修饰符可以使 ^ 和 $ 锚点匹配每一行的开头和结尾,而不仅仅是整个字符串的开头和结尾。

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := `Line 1
Line 2
Line 3`
​
    re := regexp.MustCompile(`^Line \d+$`)
    match := re.MatchString(str)
    fmt.Println(match) // false
​
    re = regexp.MustCompile(`(?m)^Line \d+$`)
    match = re.MatchString(str)
    fmt.Println(match) // true
}
复制代码

在上面的示例中,正则表达式 (?m)^Line \d+使用了m修饰符,使和使用了 m 修饰符,使 ^ 和使用了m修饰符,使和 锚点匹配每一行的开头和结尾。

8.7.3 s 修饰符(单行模式)

使用 s 修饰符可以使 . 元字符匹配包括换行符在内的任意字符。

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello\nWorld!"
​
    re := regexp.MustCompile(`Hello.World!`)
    match := re.MatchString(str)
    fmt.Println(match) // false
​
    re = regexp.MustCompile(`(?s)Hello.World!`)
    match = re.MatchString(str)
    fmt.Println(match) // true
}
复制代码

在上面的示例中,正则表达式 (?s)Hello.World! 使用了 s 修饰符,使 . 元字符可以匹配包括换行符在内的任意字符。

修饰符可以在正则表达式中使用括号和 ? 符号的形式,如 (?i)、(?m) 和 (?s)。它们可以单独使用,也可以组合使用,以适应特定的匹配需求。

8.7.4 x 修饰符(忽略空白字符)

使用 x 修饰符可以在正则表达式中忽略空白字符,包括空格、制表符和换行符。这样可以使正则表达式更易读和维护。

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello World!"
​
    re := regexp.MustCompile(`Hello   World!`)
    match := re.MatchString(str)
    fmt.Println(match) // false
​
    re = regexp.MustCompile(`(?x)Hello   World!`)
    match = re.MatchString(str)
    fmt.Println(match) // true
}
复制代码

在上面的示例中,正则表达式 (?x)Hello World! 使用了 x 修饰符,忽略了正则表达式中的空白字符。这样可以使正则表达式更易读,减少了空格的影响。

8.7.5 U 修饰符(非贪婪模式)

使用 U 修饰符可以将匹配模式设置为非贪婪模式,即尽可能少地匹配字符。

package main
​
import (
    "fmt"
    "regexp"
)
​
func main() {
    str := "Hello World!"
​
    re := regexp.MustCompile(`H.*o`)
    match := re.FindString(str)
    fmt.Println(match) // Hello World!
​
    re = regexp.MustCompile(`H.*?o`)
    match = re.FindString(str)
    fmt.Println(match) // Hello
}
复制代码

在上面的示例中,正则表达式 H.o 使用了贪婪模式,匹配了从 "H" 到最后一个 "o" 的最长字符串。而正则表达式 H. ?o 使用了 U 修饰符,将匹配模式设置为非贪婪模式,只匹配了从 "H" 到第一个 "o" 的最短字符串。

9. 总结

通过本文的介绍,希望你现在能够对于 Golang 正则表达式语法有更深入的了解。正则表达式是一个强大的字符串匹配工具,在文本处理、数据清洗、表单验证等方面都有广泛的应用。掌握正则表达式语法,可以提高大家的字符串处理效率和灵活性。希望本文能够对大家在 Golang 中使用正则表达式提供帮助,并激发大家对正则表达式的进一步探索。

以上就是一文带你全面掌握Go语言中的正则表达式的详细内容,更多关于Go语言正则表达式的资料请关注其它相关文章!

原文地址:https://juejin.cn/post/7231806316699877413
关闭

用微信“扫一扫”