golang正則之命名分組方式

正則中有分組這個功能,在golang中也可以使用命名分組。

一次匹配的情況

場景還原如下:

有一行文本,格式為:姓名 年齡 郵箱地址

請將其轉換為一個map

代碼實現如下:

str := `Alice 20 [email protected]`
// 使用命名分組,顯得更清晰
re := regexp.MustCompile(`(?P<name>[a-zA-Z]+)\s+(?P<age>\d+)\s+(?P<email>\[email protected]\w+(?:\.\w+)+)`)
match := re.FindStringSubmatch(str)
groupNames := re.SubexpNames()
fmt.Printf("%v, %v, %d, %d\n", match, groupNames, len(match), len(groupNames))
result := make(map[string]string)
// 轉換為map
for i, name := range groupNames {
    if i != 0 && name != "" { // 第一個分組為空(也就是整個匹配)
        result[name] = match[i]
    }
}
prettyResult, _ := json.MarshalIndent(result, "", "  ")
fmt.Printf("%s\n", prettyResult)

輸出為:

[Alice 20 [email protected] Alice 20 [email protected]], [ name age email], 4, 4
{
  "age": "20",
  "email": "[email protected]",
  "name": "Alice"
}

註意 [ name age email]有4個元素, 第一個為””。

多次匹配的情況

接上面的例子,實現一個更貼近現實的需求:

有一個文件, 內容大致如下:

Alice 20 [email protected]
Bob 25 [email protected]
gerrylon 26 [email protected]
...
更多內容

和上面一樣, 不過這次轉出來是一個slice of map, 也就是多個map。

代碼如下:

// 文件內容直接用字符串表示
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>\[email protected]\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))

輸出為:

[
  {
    "age": "20",
    "email": "[email protected]",
    "name": "Alice"
  },
  {
    "age": "25",
    "email": "[email protected]",
    "name": "Bob"
  },
  {
    "age": "26",
    "email": "[email protected]",
    "name": "gerrylon"
  }
]

總結

使用命名分組可以使正則表示的意義更清晰。

轉換為map更加符合人類的閱讀習慣,不過比一般的根據索引取分組值麻煩一些。

補充:golang 正則分組匹配多個值

看代碼吧~

import (
   "encoding/json"
   "fmt"
   "regexp"
)
str := `9x_xx:995:88`  // `9x_xx:995`
// 使用命名分組,一次匹配多個值
re := regexp.MustCompile(`(?P<fname>\w+):+(?P<mod>[1-9]*):*(?P<strlen>[0-9]*)`)
match := re.FindStringSubmatch(str)
groupNames := re.SubexpNames()
fmt.Printf("%v, %v, %d, %d\n", match, groupNames, len(match), len(groupNames))
 
result := make(map[string]string)
if len(match) == len(groupNames) {
   // 轉換為map
   for i, name := range groupNames {
      if i != 0 && name != "" { // 第一個分組為空(也就是整個匹配)
         result[name] = match[i]
      }
   }
}
prettyResult, _ := json.MarshalIndent(result, "", "  ") 
fmt.Printf("%s\n", prettyResult)

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。