diff --git a/zh/07.3.md b/zh/07.3.md index 74df4f1b..753c28ea 100644 --- a/zh/07.3.md +++ b/zh/07.3.md @@ -11,9 +11,9 @@ Go语言通过`regexp`标准包为正则表达式提供了官方支持,如果 `regexp`包中含有三个函数用来判断是否匹配,如果匹配返回true,否则返回false ```Go - func Match(pattern string, b []byte) (matched bool, error error) - func MatchReader(pattern string, r io.RuneReader) (matched bool, error error) - func MatchString(pattern string, s string) (matched bool, error error) +func Match(pattern string, b []byte) (matched bool, error error) +func MatchReader(pattern string, r io.RuneReader) (matched bool, error error) +func MatchString(pattern string, s string) (matched bool, error error) ``` 上面的三个函数实现了同一个功能,就是判断`pattern`是否和输入源匹配,匹配的话就返回true,如果解析正则出错则返回error。三个函数的输入源分别是byte slice、RuneReader和string。 @@ -21,26 +21,26 @@ Go语言通过`regexp`标准包为正则表达式提供了官方支持,如果 如果要验证一个输入是不是IP地址,那么如何来判断呢?请看如下实现 ```Go - func IsIP(ip string) (b bool) { - if m, _ := regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$", ip); !m { - return false - } - return true +func IsIP(ip string) (b bool) { + if m, _ := regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$", ip); !m { + return false } + return true +} ``` 可以看到,`regexp`的pattern和我们平常使用的正则一模一样。再来看一个例子:当用户输入一个字符串,我们想知道是不是一次合法的输入: ```Go - func main() { - if len(os.Args) == 1 { - fmt.Println("Usage: regexp [string]") - os.Exit(1) - } else if m, _ := regexp.MatchString("^[0-9]+$", os.Args[1]); m { - fmt.Println("数字") - } else { - fmt.Println("不是数字") - } +func main() { + if len(os.Args) == 1 { + fmt.Println("Usage: regexp [string]") + os.Exit(1) + } else if m, _ := regexp.MatchString("^[0-9]+$", os.Args[1]); m { + fmt.Println("数字") + } else { + fmt.Println("不是数字") } +} ``` 在上面的两个小例子中,我们采用了Match(Reader|String)来判断一些字符串是否符合我们的描述需求,它们使用起来非常方便。 @@ -50,52 +50,52 @@ Match模式只能用来对字符串的判断,而无法截取字符串的一部 我们经常需要一些爬虫程序,下面就以爬虫为例来说明如何使用正则来过滤或截取抓取到的数据: ```Go - package main +package main - import ( - "fmt" - "io/ioutil" - "net/http" - "regexp" - "strings" - ) +import ( + "fmt" + "io/ioutil" + "net/http" + "regexp" + "strings" +) - func main() { - resp, err := http.Get("http://www.baidu.com") - if err != nil { - fmt.Println("http get error.") - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - fmt.Println("http read error") - return - } - - src := string(body) - - //将HTML标签全转换成小写 - re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllStringFunc(src, strings.ToLower) - - //去除STYLE - re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") - - //去除SCRIPT - re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") - - //去除所有尖括号内的HTML代码,并换成换行符 - re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllString(src, "\n") - - //去除连续的换行符 - re, _ = regexp.Compile("\\s{2,}") - src = re.ReplaceAllString(src, "\n") - - fmt.Println(strings.TrimSpace(src)) +func main() { + resp, err := http.Get("http://www.baidu.com") + if err != nil { + fmt.Println("http get error.") } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("http read error") + return + } + + src := string(body) + + //将HTML标签全转换成小写 + re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") + src = re.ReplaceAllStringFunc(src, strings.ToLower) + + //去除STYLE + re, _ = regexp.Compile("\\") + src = re.ReplaceAllString(src, "") + + //去除SCRIPT + re, _ = regexp.Compile("\\") + src = re.ReplaceAllString(src, "") + + //去除所有尖括号内的HTML代码,并换成换行符 + re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") + src = re.ReplaceAllString(src, "\n") + + //去除连续的换行符 + re, _ = regexp.Compile("\\s{2,}") + src = re.ReplaceAllString(src, "\n") + + fmt.Println(strings.TrimSpace(src)) +} ``` 从这个示例可以看出,使用复杂的正则首先是Compile,它会解析正则表达式是否合法,如果正确,那么就会返回一个Regexp,然后就可以利用返回的Regexp在任意的字符串上面执行需要的操作。 @@ -103,144 +103,144 @@ Match模式只能用来对字符串的判断,而无法截取字符串的一部 解析正则表达式的有如下几个方法: ```Go - func Compile(expr string) (*Regexp, error) - func CompilePOSIX(expr string) (*Regexp, error) - func MustCompile(str string) *Regexp - func MustCompilePOSIX(str string) *Regexp +func Compile(expr string) (*Regexp, error) +func CompilePOSIX(expr string) (*Regexp, error) +func MustCompile(str string) *Regexp +func MustCompilePOSIX(str string) *Regexp ``` CompilePOSIX和Compile的不同点在于POSIX必须使用POSIX语法,它使用最左最长方式搜索,而Compile是采用的则只采用最左方式搜索(例如[a-z]{2,4}这样一个正则表达式,应用于"aa09aaa88aaaa"这个文本串时,CompilePOSIX返回了aaaa,而Compile的返回的是aa)。前缀有Must的函数表示,在解析正则语法的时候,如果匹配模式串不满足正确的语法则直接panic,而不加Must的则只是返回错误。 在了解了如何新建一个Regexp之后,我们再来看一下这个struct提供了哪些方法来辅助我们操作字符串,首先我们来看下面这些用来搜索的函数: ```Go - func (re *Regexp) Find(b []byte) []byte - 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 - 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 +func (re *Regexp) Find(b []byte) []byte +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 +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 ``` 上面这18个函数我们根据输入源(byte slice、string和io.RuneReader)不同还可以继续简化成如下几个,其他的只是输入源不一样,其他功能基本是一样的: ```Go - func (re *Regexp) Find(b []byte) []byte - func (re *Regexp) FindAll(b []byte, n int) [][]byte - func (re *Regexp) FindAllIndex(b []byte, n int) [][]int - func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte - func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int - func (re *Regexp) FindIndex(b []byte) (loc []int) - func (re *Regexp) FindSubmatch(b []byte) [][]byte - func (re *Regexp) FindSubmatchIndex(b []byte) []int +func (re *Regexp) Find(b []byte) []byte +func (re *Regexp) FindAll(b []byte, n int) [][]byte +func (re *Regexp) FindAllIndex(b []byte, n int) [][]int +func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte +func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int +func (re *Regexp) FindIndex(b []byte) (loc []int) +func (re *Regexp) FindSubmatch(b []byte) [][]byte +func (re *Regexp) FindSubmatchIndex(b []byte) []int ``` 对于这些函数的使用我们来看下面这个例子 ```Go - package main +package main - import ( - "fmt" - "regexp" - ) +import ( + "fmt" + "regexp" +) - func main() { - a := "I am learning Go language" +func main() { + a := "I am learning Go language" - re, _ := regexp.Compile("[a-z]{2,4}") + re, _ := regexp.Compile("[a-z]{2,4}") - //查找符合正则的第一个 - one := re.Find([]byte(a)) - fmt.Println("Find:", string(one)) + //查找符合正则的第一个 + one := re.Find([]byte(a)) + fmt.Println("Find:", string(one)) - //查找符合正则的所有slice,n小于0表示返回全部符合的字符串,不然就是返回指定的长度 - all := re.FindAll([]byte(a), -1) - fmt.Println("FindAll", all) + //查找符合正则的所有slice,n小于0表示返回全部符合的字符串,不然就是返回指定的长度 + all := re.FindAll([]byte(a), -1) + fmt.Println("FindAll", all) - //查找符合条件的index位置,开始位置和结束位置 - index := re.FindIndex([]byte(a)) - fmt.Println("FindIndex", index) + //查找符合条件的index位置,开始位置和结束位置 + index := re.FindIndex([]byte(a)) + fmt.Println("FindIndex", index) - //查找符合条件的所有的index位置,n同上 - allindex := re.FindAllIndex([]byte(a), -1) - fmt.Println("FindAllIndex", allindex) + //查找符合条件的所有的index位置,n同上 + allindex := re.FindAllIndex([]byte(a), -1) + fmt.Println("FindAllIndex", allindex) - re2, _ := regexp.Compile("am(.*)lang(.*)") + re2, _ := regexp.Compile("am(.*)lang(.*)") - //查找Submatch,返回数组,第一个元素是匹配的全部元素,第二个元素是第一个()里面的,第三个是第二个()里面的 - //下面的输出第一个元素是"am learning Go language" - //第二个元素是" learning Go ",注意包含空格的输出 - //第三个元素是"uage" - submatch := re2.FindSubmatch([]byte(a)) - fmt.Println("FindSubmatch", submatch) - for _, v := range submatch { - fmt.Println(string(v)) - } - - //定义和上面的FindIndex一样 - submatchindex := re2.FindSubmatchIndex([]byte(a)) - fmt.Println(submatchindex) - - //FindAllSubmatch,查找所有符合条件的子匹配 - submatchall := re2.FindAllSubmatch([]byte(a), -1) - fmt.Println(submatchall) - - //FindAllSubmatchIndex,查找所有字匹配的index - submatchallindex := re2.FindAllSubmatchIndex([]byte(a), -1) - fmt.Println(submatchallindex) + //查找Submatch,返回数组,第一个元素是匹配的全部元素,第二个元素是第一个()里面的,第三个是第二个()里面的 + //下面的输出第一个元素是"am learning Go language" + //第二个元素是" learning Go ",注意包含空格的输出 + //第三个元素是"uage" + submatch := re2.FindSubmatch([]byte(a)) + fmt.Println("FindSubmatch", submatch) + for _, v := range submatch { + fmt.Println(string(v)) } + + //定义和上面的FindIndex一样 + submatchindex := re2.FindSubmatchIndex([]byte(a)) + fmt.Println(submatchindex) + + //FindAllSubmatch,查找所有符合条件的子匹配 + submatchall := re2.FindAllSubmatch([]byte(a), -1) + fmt.Println(submatchall) + + //FindAllSubmatchIndex,查找所有字匹配的index + submatchallindex := re2.FindAllSubmatchIndex([]byte(a), -1) + fmt.Println(submatchallindex) +} ``` 前面介绍过匹配函数,Regexp也定义了三个函数,它们和同名的外部函数功能一模一样,其实外部函数就是调用了这Regexp的三个函数来实现的: ```Go - func (re *Regexp) Match(b []byte) bool - func (re *Regexp) MatchReader(r io.RuneReader) bool - func (re *Regexp) MatchString(s string) bool +func (re *Regexp) Match(b []byte) bool +func (re *Regexp) MatchReader(r io.RuneReader) bool +func (re *Regexp) MatchString(s string) bool ``` 接下里让我们来了解替换函数是怎么操作的? ```Go - 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 (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 ``` 这些替换函数我们在上面的抓网页的例子有详细应用示例, 接下来我们看一下Expand的解释: ```Go - func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte - func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte +func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte +func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte ``` 那么这个Expand到底用来干嘛的呢?请看下面的例子: ```Go - func main() { - src := []byte(` - call hello alice - hello bob - call hello eve - `) - pat := regexp.MustCompile(`(?m)(call)\s+(?P\w+)\s+(?P.+)\s*$`) - res := []byte{} - for _, s := range pat.FindAllSubmatchIndex(src, -1) { - res = pat.Expand(res, []byte("$cmd('$arg')\n"), src, s) - } - fmt.Println(string(res)) +func main() { + src := []byte(` + call hello alice + hello bob + call hello eve + `) + pat := regexp.MustCompile(`(?m)(call)\s+(?P\w+)\s+(?P.+)\s*$`) + res := []byte{} + for _, s := range pat.FindAllSubmatchIndex(src, -1) { + res = pat.Expand(res, []byte("$cmd('$arg')\n"), src, s) } + fmt.Println(string(res)) +} ``` 至此我们已经全部介绍完Go语言的`regexp`包,通过对它的主要函数介绍及演示,相信大家应该能够通过Go语言的正则包进行一些基本的正则的操作了。