【go语言】regexp包,正则表达式
Go语言 regexp 包详解
Go 语言的 regexp
包提供了对正则表达式的支持。
正则表达式(regex)是一种字符串搜索模式,用来检查一个字符串是否符合某种特定的模式,或从中提取符合某种模式的子字符串。
1. regexp
包概述
regexp
包支持对字符串的匹配、搜索和替换。它基于 RE2 正则表达式引擎,性能优异,避免了回溯带来的性能瓶颈。
该包提供了多个函数来编译正则表达式并进行匹配、查找、替换等操作。
2. 常用函数
2.1 regexp.Compile(pattern string) (*Regexp, error)
该函数用于编译正则表达式字符串 pattern
,并返回一个 Regexp
类型对象。如果正则表达式无效,会返回一个错误。
示例:
r, err := regexp.Compile(`\d+`) // 匹配一个或多个数字
if err != nil {
fmt.Println("Error compiling regex:", err)
return
}
2.2 regexp.MustCompile(pattern string) *Regexp
该函数与 Compile
类似,不过如果正则表达式不合法,它会立即 panic。适合在启动时使用,确保正则表达式是合法的。
示例:
r := regexp.MustCompile(`\d+`)
2.3 r.MatchString(s string) bool
该方法检查给定的字符串 s
是否匹配正则表达式。如果匹配,返回 true
,否则返回 false
。
示例:
r := regexp.MustCompile(`\d+`)
fmt.Println(r.MatchString("12345")) // 输出:true
fmt.Println(r.MatchString("abc")) // 输出:false
2.4 r.FindString(s string) string
该方法查找字符串中第一个匹配正则表达式的子字符串并返回。如果没有找到匹配项,则返回空字符串。
示例:
r := regexp.MustCompile(`\d+`)
fmt.Println(r.FindString("abc 12345 xyz")) // 输出:12345
2.5 r.FindAllString(s string, n int) []string
查找字符串中所有匹配正则表达式的子字符串,并返回一个字符串切片。如果 n
为 -1,则表示返回所有匹配的子串。
示例:
r := regexp.MustCompile(`\d+`)
fmt.Println(r.FindAllString("abc 123 4567 89", -1)) // 输出:[123 4567 89]
2.6 r.ReplaceAllString(s string, repl string) string
该方法用于替换字符串中所有匹配正则表达式的子字符串。repl
是替换的文本。
示例:
r := regexp.MustCompile(`\d+`)
result := r.ReplaceAllString("abc 123 4567 xyz", "#")
fmt.Println(result) // 输出:abc # # xyz
2.7 r.FindSubmatch(s string) [][]byte
查找第一个匹配的子字符串,并返回每个捕获组的内容(包括完整匹配)。返回的是一个二维字节切片,每个字节切片表示一个匹配项。
示例:
r := regexp.MustCompile(`(\d+)-(\d+)`)
match := r.FindSubmatch([]byte("abc 123-4567"))
fmt.Println(match) // 输出:[[123-4567 123 4567]]
2.8 r.Split(s string, n int) []string
根据正则表达式分割字符串,返回一个字符串切片。n
参数表示最多分割的次数。
示例:
r := regexp.MustCompile(`\s+`)
fmt.Println(r.Split("this is a test", -1)) // 输出:[this is a test]
3. 正则表达式语法
Go 语言的 regexp
包使用的是 RE2 正则表达式引擎,它支持一些常见的正则表达式语法:
.
:匹配除换行符以外的任何单个字符。^
:匹配输入字符串的开始位置。$
:匹配输入字符串的结束位置。*
:匹配前一个字符零次或多次。+
:匹配前一个字符一次或多次。?
:匹配前一个字符零次或一次。{n,m}
:匹配前一个字符至少n
次,但不超过m
次。|
:匹配左侧或右侧的表达式。()
:分组,括起来的部分作为一个整体进行匹配。[]
:字符集,匹配括号内的任何一个字符。\d
:匹配一个数字,等同于[0-9]
。\w
:匹配一个字母或数字字符,等同于[a-zA-Z0-9_]
。\s
:匹配一个空白字符(包括空格、制表符、换行符等)。
4. 示例
示例 1:检查字符串是否符合正则表达式
package main
import (
"fmt"
"regexp"
)
func main() {
// 编译正则表达式
r := regexp.MustCompile(`\d+`)
// 检查字符串是否包含数字
fmt.Println(r.MatchString("abc123")) // 输出:true
fmt.Println(r.MatchString("abc")) // 输出:false
}
示例 2:提取数字
package main
import (
"fmt"
"regexp"
)
func main() {
// 编译正则表达式
r := regexp.MustCompile(`\d+`)
// 查找字符串中的第一个数字
result := r.FindString("abc 12345 xyz")
fmt.Println(result) // 输出:12345
}
示例 3:替换字符串中的数字
package main
import (
"fmt"
"regexp"
)
func main() {
// 编译正则表达式
r := regexp.MustCompile(`\d+`)
// 替换数字为 #
result := r.ReplaceAllString("abc 123 4567 xyz", "#")
fmt.Println(result) // 输出:abc # # xyz
}
示例 4:分割字符串
package main
import (
"fmt"
"regexp"
)
func main() {
// 编译正则表达式
r := regexp.MustCompile(`\s+`)
// 按空白字符分割字符串
result := r.Split("this is a test", -1)
fmt.Println(result) // 输出:[this is a test]
}
5. 性能与注意事项
- Go 的
regexp
包非常高效,特别是在处理大数据量时,RE2 正则表达式引擎避免了回溯带来的性能问题。 - 使用正则表达式时需要谨慎,以避免过度复杂的模式导致性能瓶颈。
总结
regexp
包是 Go 语言中一个强大的工具,用于进行字符串模式匹配和替换操作。
通过使用该包,你可以灵活地处理复杂的字符串匹配任务,并且可以高效地进行文本处理。
案例
package _case
import (
"fmt"
"regexp"
)
func RegexpCase() {
test1()
fmt.Println("------------")
test2()
}
func test2() {
// 案例
pattern := `^[a-z]+\[[0-9]+\]$`
re := regexp.MustCompile(pattern)
fmt.Println(re.MatchString("asdf[22114]"))
// FindAll传入[]byte;
// FindAllString传入string
bytes := re.FindAll([]byte("haha[1234]"), -1)
fmt.Println(string(bytes[0]))
re = regexp.MustCompile(`(\d{4})-(\d{2})-(\d{2})`)
str := "今天日期:2024-10-21"
replaced := re.ReplaceAllString(str, "我日你妈的")
fmt.Println(replaced)
}
// 常见的正则表达式字符及其含义
// 锚点字符:
//
// ^:匹配字符串的开始。例如,^abc 匹配以 "abc" 开头的字符串。
// $:匹配字符串的结束。例如,abc$ 匹配以 "abc" 结尾的字符串。
// 字符匹配:
//
// .(点号):匹配任意一个字符,除了换行符。例子:a.b 可以匹配 "a_b"、"a1b",但不能匹配 "ab"。
// [ ]:定义字符集合,匹配集合中的任意一个字符。例如,[abc] 匹配 "a"、"b" 或 "c"。
// [^ ]:排除字符集合,匹配集合外的任意字符。例如,[^abc] 匹配除 "a"、"b" 和 "c" 以外的任意字符。
// 量词:
//
// *:匹配前面的元素 0 次或多次。例如,a* 匹配空字符串、"a"、"aa" 等。
// +:匹配前面的元素 1 次或多次。例如,a+ 匹配 "a"、"aa"、"aaa" 等。
// ?:匹配前面的元素 0 次或 1 次。例如,a? 匹配空字符串或 "a"。
// {n}:匹配前面的元素恰好 n 次。例如,a{3} 匹配 "aaa"。
// {n,}:匹配前面的元素至少 n 次。例如,a{2,} 匹配 "aa"、"aaa"、"aaaa" 等。
// {n,m}:匹配前面的元素至少 n 次,但不超过 m 次。例如,a{2,4} 匹配 "aa"、"aaa" 或 "aaaa"。
// 字符类:
//
// \d:匹配任意数字,等同于 [0-9]。
// \D:匹配非数字字符,等同于 [^0-9]。
// \w:匹配字母、数字或下划线,等同于 [a-zA-Z0-9_]。
// \W:匹配非字母、数字或下划线字符,等同于 [^a-zA-Z0-9_]。
// \s:匹配任意空白字符(包括空格、制表符、换行符等)。
// \S:匹配任意非空白字符。
// 分组和选择:
//
// ( ):定义捕获组,匹配的内容可以在后续操作中引用。例如,(abc) 匹配 "abc"。
// |:逻辑或,表示左右两边任意一个匹配即可。例如,a|b 匹配 "a" 或 "b"。
// 反斜杠转义:
//
// \:反斜杠用于转义特殊字符。例如,\. 匹配字符 ".",而不是表示任意字符的点号。
// 特殊字符类:
//
// \b:匹配单词边界。例如,\bword\b 匹配完整的 "word",但不会匹配 "sword" 中的 "word"。
// \B:匹配非单词边界。
// 举例说明
// ^\d{3}-\d{2}-\d{4}$:匹配符合格式的社会保障号(比如 "123-45-6789")。
//
// ^:匹配字符串的开始。
// \d{3}:匹配 3 个数字。
// -:匹配字符 -。
// \d{2}:匹配 2 个数字。
// \d{4}:匹配 4 个数字。
// $:匹配字符串的结束。
// ([A-Z][a-z]+):匹配以大写字母开头的单词(如 "Apple")。
//
// [A-Z]:匹配一个大写字母。
// [a-z]+:匹配一个或多个小写字母。
// ( ):定义捕获组。
func test1() {
// Compile解析并返回一个正则表达式。如果成功返回,该Regexp就可用于匹配文本。
re := regexp.MustCompile(".com")
// FindString
fmt.Println(re.FindString("(cainiao.com)"))
fmt.Println(re.FindString("(cainiao.dz)"))
fmt.Println(re.FindString("(cainiao1.com1)"))
// FindStringIndex return符合正则表达式的字符的索引,左闭右开
fmt.Println(re.FindStringIndex("google.com"))
fmt.Println(re.FindStringIndex("abc.org"))
fmt.Println(re.FindStringIndex("fb.com"))
// FindStringSubmatch 该方法返回具有最左边匹配项和匹配组的字符串。找不到返回空字符串
// ([a-z]+)称为匹配组
re1 := regexp.MustCompile("f([a-z]+)ing")
re2 := regexp.MustCompile("f([a-z]+)ing")
fmt.Println(re1.FindStringSubmatch("flying1"))
fmt.Println(re1.FindStringSubmatch("abcfloatingxyz"))
fmt.Println(re2.FindStringSubmatch("flying1"))
fmt.Println(re2.FindStringSubmatch("abcfloatingxyz"))
fmt.Println("-----------")
pattern := "([a-z]+)"
regex := regexp.MustCompile(pattern)
// FindStringSubmatch 只会返回一个 包含的匹配结果,和一个 匹配组 合成的一个切片
// len:2
matches := regex.FindStringSubmatch("apple banana orange")
fmt.Println(matches[0])
fmt.Println(matches[1])
//fmt.Println(matches[2])
//fmt.Println(matches[3])
// 如果想要匹配所有符合正则表达式的值,要用FindAllStringSubmatch
// n为-1时,匹配所有符合条件的字符串,n不为-1时,表示只匹配n次
// 会返回一个二维切片
matches1 := regex.FindAllStringSubmatch("apple banana orange", -1)
fmt.Println(matches1)
// 遍历匹配结果
for i, match := range matches1 {
fmt.Printf("匹配 %d 结果:%s\n", i, match[0])
fmt.Printf("捕获组 %d 结果:%s\n", i, match[1])
}
// 命名捕获组语法
// (?P<name>pattern)
// name:捕获组名称,pattern:捕获组模式
pattern = "(?P<fruit>[a-z]+)"
// 建立正则表达式
re3 := regexp.MustCompile(pattern)
// 匹配单个值
matches = re3.FindStringSubmatch("apple banana orange")
fruit := matches[re3.SubexpIndex("fruit")]
fmt.Println(fruit)
// 匹配多个值
matches1 = re3.FindAllStringSubmatch("apple banana orange", -1)
for i, match := range matches1 {
fruit = match[re3.SubexpIndex("fruit")]
fmt.Printf("匹配结果%d:%s\n", i, match[0])
fmt.Printf("捕获组组%d:%s\n", i, fruit)
}
// regexp.Match() 判断在[]byte中能否找到给定正则表达式的值,返回bool和err
// .*:表示任意数量的任意字符(包括 0 个字符),可以匹配任何字符,除了换行符
// "^abc.*z$"含义:
// 匹配以abc开头中间任意个数任意类型字符(除换行符外)以z结尾的字符串
matched, err := regexp.Match("^abc.*z$", []byte("abcdeeffgz"))
fmt.Println(matched, err)
matched, err = regexp.Match("^abc.*z$", []byte("bcdefgz"))
fmt.Println(matched, err)
// regexp.MatchString()类似regexp.Match()不过他只传字符串,而Match只传[]byte
}