在 Go 中要使用规则表达式获取比对成功的部份、取代等任务,都得将规则表达式编译为Regexp
才可以:
func Compile(expr string) (*Regexp, error)
func CompilePOSIX(expr string) (*Regexp, error)
func MustCompile(str string) *Regexp
func MustCompilePOSIX(str string) *Regexp
POSIX 结尾的函数,表示规则表达式必须符合 POSIX ERE (egrep) 语法,Must 开头的函数,表示解析错误的话会 panic。
解析成功的话,返回*Regexp
,之后就是比对任务了,不用再处理错误。例如:
package main
import (
"fmt"
"regexp"
)
func main() {
re, err := regexp.Compile(`\d{4}-\d{6}`)
fmt.Println(re, err)
matched := re.MatchString("0970-168168")
fmt.Println(matched)
matched = re.MatchString("Phone: 0970-168168")
fmt.Println(matched)
}
寻找符合项目
如果想找出最左边第一个符合项目,可以使用 Find 开头的方法版本:
func (re *Regexp) Find(b []byte) []byte
func (re *Regexp) FindIndex(b []byte) (loc []int)
func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)
func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int
func (re *Regexp) FindString(s string) string
func (re *Regexp) FindStringIndex(s string) (loc []int)
func (re *Regexp) FindStringSubmatch(s string) []string
func (re *Regexp) FindStringSubmatchIndex(s string) []int
func (re *Regexp) FindSubmatch(b []byte) [][]byte
func (re *Regexp) FindSubmatchIndex(b []byte) []int
有 Index 字样的版本,返回的[]int
中会有两个元素,分别是符合项目的字节开头与结尾索引位置,例如:
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`foo.?`)
fmt.Printf("%q\n", re.FindString("seafood fool")) // "food"
fmt.Printf("%v\n", re.FindStringIndex("seafood fool")) // [3 7]
}
有 Submatch 字样的方法,是用来支持分组。例如:
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(\d{4})-(\d{6})`)
// ["0970-666888" "0970" "666888"]
fmt.Printf("%q\n", re.FindStringSubmatch("0970-666888"))
}
如果要找出全部的符合项目呢?在这之前来看看如何用规则表达式来切割子字符串,这可以使用Regexp
的Split
方法,它的第二个参数可以指定至少切割几个子字符串,若指定小于 0 的数,会切出全部的子字符串:
package main
import (
"fmt"
"regexp"
)
func main() {
re, _ := regexp.Compile(`\d`)
fmt.Println(re.Split("Justin1Monica2Irene", 1)) // [Justin1Monica2Irene]
fmt.Println(re.Split("Justin1Monica2Irene", 2)) // [Justin Monica2Irene]
fmt.Println(re.Split("Justin1Monica2Irene", 3)) // [Justin Monica Irene]
fmt.Println(re.Split("Justin1Monica2Irene", -1)) // [Justin Monica Irene]
}
Regexp
提供的 Find 开头的方法,有不少是这种指定模式,例如:
func (re *Regexp) FindAll(b []byte, n int) [][]byte
func (re *Regexp) FindAllIndex(b []byte, n int) [][]int
func (re *Regexp) FindAllString(s string, n int) []string
func (re *Regexp) FindAllStringIndex(s string, n int) [][]int
func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string
func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int
func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int
因此,要找出全部的符合项目,一个例子如下:
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(\d{4})-(\d{6})`)
// 分行显示 "0970-666888" 与 "0970-168168"
for _, submatch := range re.FindAllString("0970-666888, 0970-168168", -1) {
fmt.Printf("%q\n", submatch)
}
}
底下则是捕捉分组的版本:
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(\d{4})-(\d{6})`)
// 分行显示 "0970-666888" 与 "0970-168168"
for _, submatch := range re.FindAllStringSubmatch("0970-666888, 0970-168168", -1) {
fmt.Printf("%q\n", submatch)
}
}
取代相符项目
若要进行取代,使用的是 Replace 开头的方法:
func (re *Regexp) ReplaceAll(src, repl []byte) []byte
func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte
func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte
func (re *Regexp) ReplaceAllLiteralString(src, repl string) string
func (re *Regexp) ReplaceAllString(src, repl string) string
func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string
有 Func 结尾的方法,表示可以指定函数,该函数接收符合的项目,由函数决定用什么取代。没有 Literal 字样的方法,repl
的部份支持分组捕捉,分组计数表示方式是${n}
,例如:
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(^[a-zA-Z]+\d*)@([a-z]+?.)com`)
// 显示 caterpillar@openhome.cc
fmt.Println(re.ReplaceAllString("caterpillar@openhome.com", "${1}@${2}cc"))
}
如果使用了(?P<name>…)
为分组命名,可以使用${name}
,例如:
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<user>^[a-zA-Z]+\d*)@(?P<preCom>[a-z]+?.)com`)
// 显示 caterpillar@openhome.cc
fmt.Println(re.ReplaceAllString("caterpillar@openhome.com", "${user}@${preCom}cc"))
}
虽然说方才的${2}
也可以写为$2
,然而之后接上其他文本的话,例如$2cc
,就会被认为是分组命名,类似地,方才的${preCom}
写成$preCom
也可以,不过之后接上其他文本的话,例如$preComcc
就会被认为名称是 preComcc,建议还是加上{}
。
Replace 方法中具有 Literal 字样的,就是直接把$
当成字面文本来解释:
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`(?P<user>^[a-zA-Z]+\d*)@(?P<preCom>[a-z]+?.)com`)
// $user@${preCom}cc
fmt.Println(re.ReplaceAllLiteralString("caterpillar@openhome.com", "$user@${preCom}cc"))
}