Go 正则分组提取

25 min read
// 文件内容直接用字符串表示
usersStr := `
    Alice 20 [email protected]
    Bob 25 [email protected]
    gerrylon 26 [email protected]
`

userRe := regexp.MustCompile(`(?P<name>[a-zA-Z]+)\s+(?P<age>\d+)\s+(?P<email>\w+@\w+(?:\.\w+)+)`)

// 这里要用FindAllStringSubmatch,找到所有的匹配
users := userRe.FindAllStringSubmatch(usersStr, -1)

groupNames := userRe.SubexpNames()
var result []map[string]string // slice of map

// 循环所有行
for _, user := range users {
    m := make(map[string]string)

    // 对每一行生成一个map
    for j, name := range groupNames {
        if j != 0 && name != "" {
            m[name] = strings.TrimSpace(user[j])
        }
    }
    result = append(result, m)
}

prettyResult, _ := json.MarshalIndent(result, "", "  ")
fmt.Println(string(prettyResult))

封装正则分组匹配提取

/**
 * Parses url with the given regular expression and returns the 
 * group values defined in the expression.
 *
 */
func getParams(regEx, url string) (paramsMap map[string]string) {

    var compRegEx = regexp.MustCompile(regEx)
    match := compRegEx.FindStringSubmatch(url)

    paramsMap = make(map[string]string)
    for i, name := range compRegEx.SubexpNames() {
        if i > 0 && i <= len(match) {
            paramsMap[name] = match[i]
        }
    }
    return paramsMap
}

使用

params := getParams(`(?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2})`, `2015-05-27`)
fmt.Println(params)

map[Year:2015 Month:05 Day:27]

Go 正则的基本使用

  1. 判断是否匹配
package main 
import(
    "fmt"
    "regexp"
)

func main() {
    matched, err := regexp.MatchString("foo.*", "seafood")
    fmt.Println(matched, err)
    matched, err = regexp.MatchString("bar.*", "seafood")
    fmt.Println(matched, err)
    matched, err = regexp.MatchString("a(b", "seafood")
    fmt.Println(matched, err)
}

// true <nil>
// false <nil>
// false error parsing regexp: missing closing ): `a(b`
  1. 通过正则获取内容
package main 
import(
    "fmt"
    "regexp"
)

func main() {
    re := regexp.MustCompile("a.")
    fmt.Println(re.FindAllString("paranormal", -1)) //-1表示返回所有匹配的值,[ar an al]
    fmt.Println(re.FindAllString("paranormal", 2)) //2表示返回2个匹配的值,[ar an]
    fmt.Println(re.FindAllString("paranormal", 1)) //1表示返回1个匹配的值,[ar]
    fmt.Println(re.FindAllString("paranormal", 0)) //0表示返回0个匹配的值,[]
    fmt.Println(re.FindAllString("graal", -1)) //[aa]
    fmt.Println(re.FindAllString("none", -1)) //[]
}

正则匹配字符

正则表达式中,“.”(点符号)匹配的是除了换行符“\n”以外的所有字符。但有时候我们需要匹配包括换行符在内的字符,经过一番搜索,发现了几种正则表达式匹配任意字符(包括换行符)的方法。

可以用 ([\s\S]*) ,也可以用 “([\d\D])”、“([\w\W])” 来匹配,就可以匹配包括换行符在内的任意字符

  下面是正则表达式元字符介绍

  “^” :^会匹配行或者字符串的起始位置,有时还会匹配整个文档的起始位置。

  “$” :$会匹配行或字符串的结尾

  而且被匹配的字符必须是以This开头有空格也不行,必须以Regex结尾,也不能有空格与其它字符

  “\b” :不会消耗任何字符只匹配一个位置,常用于匹配单词边界 如 我想从字符串中“This is Regex”匹配单独的单词 “is” 正则就要写成 “\bis\b”

  “\d”: 匹配数字,

  例如要匹配一个固定格式的电话号码以0开头前4位后7位,如0737-5686123 正则:^0\d\d\d-\d\d\d\d\d\d\d$ 这里只是为了介绍“\d”字符,实际上有更好的写法会在 下面介绍。

  “\w”:匹配字母,数字,下划线。

  例如我要匹配“a2345BCD__TTz” 正则:“\w+” 这里的“+”字符为一个量词指重复的次数,稍后会详细介绍。

  “\s”:匹配空格

  例如字符 “a b c” 正则:“\w\s\w\s\w” 一个字符后跟一个空格,如有字符间有多个空格直接把“\s” 写成 “\s+” 让空格重复

  “.”:匹配除了换行符以外的任何字符

  这个算是“\w”的加强版了“\w”不能匹配 空格 如果把字符串加上空格用“\w”就受限了,看下用 “.”是如何匹配字符“a23 4 5 B C D__TTz” 正则:“.+

正则在线工具

https://regex101.com/

Demo 提取微信公众号封面和标题

func getParams(regEx, url string) (paramsMap map[string]string) {

	var compRegEx = regexp.MustCompile(regEx)
	match := compRegEx.FindStringSubmatch(url)

	paramsMap = make(map[string]string)
	for i, name := range compRegEx.SubexpNames() {
		if i > 0 && i <= len(match) {
			paramsMap[name] = match[i]
		}
	}
	return paramsMap
}

func search(url string) map[string]string {
	r := "var msg_title = '(?P<title>.+)'.html\\(false\\);[\\s\\S]*var cdn_url_1_1 = \"(?P<cover>http.+)\""
	res, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {

		}
	}(res.Body)
	if res.StatusCode != 200 {
		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
	}

	bodyBytes, err := io.ReadAll(res.Body)

	html := string(bodyBytes)

	params := getParams(r, html)
	fmt.Println(params)
	return params

}