Go 語言入門很簡單:正則表示式
前言
在計算中,我們經常需要將特定模式的字元或字元子集匹配為另一個字串中的字串。此技術用於使用特別的語法來搜尋給定字串中的特定字符集。比如郵件、手機號、身份證號等等。
如果搜尋到的模式匹配,或者在目標字串中找到給定的子集,則搜尋被稱為成功;否則被認為是不成功的。
那麼此時該用到正則表示式了。
什麼是正則表示式
正則表示式(或 RegEx)是一個特殊的字元序列,它定義了用於匹配特定文字的搜尋模式。在 Golang 中,有一個內建的正則表示式包: regexp
包,其中包含所有操作列表,如過濾、修改、替換、驗證或提取。
正則表示式可以用於文字搜尋和更高階的文字操作。正則表示式內置於 grep 和 sed 等工具,vi 和 emacs 等文字編輯器,Go、Java 和 Python 等程式語言中。
表示式的語法主要遵循這些流行語言中使用的已建立的 RE2 語法。 RE2 語法是 PCRE 的一個子集,有各種注意事項。
以下是正則表示式模式表格:
Go 語言的 regexp
包中有幾個典型的函式:
-
MatchString()
-
Compile()
-
FindString()
-
FindAllString()
-
FindStringIndex()
-
FindAllStringIndex()
-
FindStringSubmatch()
-
Split()
-
ReplaceAllString
-
ReplaceAllStringFunc()
現在來一一看看這些函式的使用
MatchString 函式
MatchString()
函式報告作為引數傳遞的字串是否包含正則表示式模式的任何匹配項。
package main
import (
"fmt"
"log"
"regexp"
)
func main() {
words := [...]string{"Seven", "even", "Maven", "Amen", "eleven"}
for _, word := range words {
found, err := regexp.MatchString(".even", word)
if err != nil {
log.Fatal(err)
}
if found {
fmt.Printf("%s matches\n", word)
} else {
fmt.Printf("%s does not match\n", word)
}
}
}
複製程式碼
執行該程式碼:
Seven matches
even does not match
Maven does not match
Amen does not match
eleven matches
複製程式碼
但同時我們能看到編輯器有提示:
編譯器已經開始提醒我們, MatchString
直接使用效能很差,所以考慮使用 regexp.Compile
函式。
Compile 函式
Compile
函式解析正則表示式,如果成功,則返回可用於匹配文字的 Regexp 物件。編譯的正則表示式產生更快的程式碼。
MustCompile
函式是一個便利函式,它編譯正則表示式並在無法解析表示式時發生 panic。
package main
import (
"fmt"
"log"
"regexp"
)
func main() {
words := [...]string{"Seven", "even", "Maven", "Amen", "eleven"}
re, err := regexp.Compile(".even")
if err != nil {
log.Fatal(err)
}
for _, word := range words {
found := re.MatchString(word)
if found {
fmt.Printf("%s matches\n", word)
} else {
fmt.Printf("%s does not match\n", word)
}
}
}
複製程式碼
在程式碼示例中,我們使用了編譯的正則表示式。
re, err := regexp.Compile(".even")
複製程式碼
即使用 Compile
編譯正則表示式。然後在返回的正則表示式物件上呼叫 MatchString
函式:
found := re.MatchString(word)
複製程式碼
執行程式,能看到同樣的程式碼:
Seven matches
even does not match
Maven does not match
Amen does not match
eleven matches
複製程式碼
MustCompile 函式
package main
import (
"fmt"
"regexp"
)
func main() {
words := [...]string{"Seven", "even", "Maven", "Amen", "eleven"}
re := regexp.MustCompile(".even")
for _, word := range words {
found := re.MatchString(word)
if found {
fmt.Printf("%s matches\n", word)
} else {
fmt.Printf("%s does not match\n", word)
}
}
}
複製程式碼
FindAllString 函式
FindAllString
函式返回正則表示式的所有連續匹配的切片。
package main
import (
"fmt"
"os"
"regexp"
)
func main() {
var content = `Foxes are omnivorous mammals belonging to several genera
of the family Canidae. Foxes have a flattened skull, upright triangular ears,
a pointed, slightly upturned snout, and a long bushy tail. Foxes live on every
continent except Antarctica. By far the most common and widespread species of
fox is the red fox.`
re := regexp.MustCompile("(?i)fox(es)?")
found := re.FindAllString(content, -1)
fmt.Printf("%q\n", found)
if found == nil {
fmt.Printf("no match found\n")
os.Exit(1)
}
for _, word := range found {
fmt.Printf("%s\n", word)
}
}
複製程式碼
在程式碼示例中,我們找到了單詞 fox 的所有出現,包括它的複數形式。
re := regexp.MustCompile("(?i)fox(es)?")
複製程式碼
使用 (?i) 語法,正則表示式不區分大小寫。 (es)?表示“es”字元可能包含零次或一次。
found := re.FindAllString(content, -1)
複製程式碼
我們使用 FindAllString
查詢所有出現的已定義正則表示式。第二個引數是要查詢的最大匹配項; -1 表示搜尋所有可能的匹配項。
執行結果:
["Foxes" "Foxes" "Foxes" "fox" "fox"]
Foxes
Foxes
Foxes
fox
fox
複製程式碼
FindAllStringIndex 函式
package main
import (
"fmt"
"regexp"
)
func main() {
var content = `Foxes are omnivorous mammals belonging to several genera
of the family Canidae. Foxes have a flattened skull, upright triangular ears,
a pointed, slightly upturned snout, and a long bushy tail. Foxes live on every
continent except Antarctica. By far the most common and widespread species of
fox is the red fox.`
re := regexp.MustCompile("(?i)fox(es)?")
idx := re.FindAllStringIndex(content, -1)
for _, j := range idx {
match := content[j[0]:j[1]]
fmt.Printf("%s at %d:%d\n", match, j[0], j[1])
}
}
複製程式碼
在程式碼示例中,我們在文字中找到所有出現的 fox 單詞及其索引。
<code data-type="codeline"> Foxes at 0:5</code><code data-type="codeline"> Foxes at 81:86</code><code data-type="codeline"> Foxes at 196:201</code><code data-type="codeline"> fox at 296:299</code><code data-type="codeline"> fox at 311:314</code>
複製程式碼
Split 函式
Split
函式將字串切割成由定義的正則表示式分隔的子字串。它返回這些表示式匹配之間的子字串切片。
package main
import (
"fmt"
"log"
"regexp"
"strconv"
)
func main() {
var data = `22, 1, 3, 4, 5, 17, 4, 3, 21, 4, 5, 1, 48, 9, 42`
sum := 0
re := regexp.MustCompile(",\s*")
vals := re.Split(data, -1)
for _, val := range vals {
n, err := strconv.Atoi(val)
sum += n
if err != nil {
log.Fatal(err)
}
}
fmt.Println(sum)
}
複製程式碼
在程式碼示例中,我們有一個逗號分隔的值列表。我們從字串中擷取值並計算它們的總和。
re := regexp.MustCompile(",\s*")
複製程式碼
正則表示式包括一個逗號字元和任意數量的相鄰空格。
vals := re.Split(data, -1)
複製程式碼
我們得到了值的一部分。
for _, val := range vals {
n, err := strconv.Atoi(val)
sum += n
if err != nil {
log.Fatal(err)
}
}
複製程式碼
我們遍歷切片並計算總和。切片包含字串;因此,我們使用 strconv.Atoi
函式將每個字串轉換為整數。
執行程式碼:
複製程式碼
Go 正則表示式捕獲組
圓括號 () 用於建立捕獲組。這允許我們將量詞應用於整個組或將交替限制為正則表示式的一部分。為了找到捕獲組(Go 使用術語子表示式),我們使用 FindStringSubmatch
函式。
package main
import (
"fmt"
"regexp"
)
func main() {
websites := [...]string{"webcode.me", "zetcode.com", "freebsd.org", "netbsd.org"}
re := regexp.MustCompile("(\w+)\.(\w+)")
for _, website := range websites {
parts := re.FindStringSubmatch(website)
for i, _ := range parts {
fmt.Println(parts[i])
}
fmt.Println("---------------------")
}
}
複製程式碼
在程式碼示例中,我們使用組將域名分為兩部分。
re := regexp.MustCompile("(\w+)\.(\w+)")
複製程式碼
我們用括號定義了兩個組。
parts := re.FindStringSubmatch(website)
複製程式碼
FindStringSubmatch
返回包含匹配項的字串切片,包括來自捕獲組的字串。
執行程式碼:
<code data-type="codeline"> $ go run capturegroups.go </code><code data-type="codeline"> webcode.me</code><code data-type="codeline"> webcode</code><code data-type="codeline"> me</code><code data-type="codeline"> ---------------------</code><code data-type="codeline"> zetcode.com</code><code data-type="codeline"> zetcode</code><code data-type="codeline"> com</code><code data-type="codeline"> ---------------------</code><code data-type="codeline"> freebsd.org</code><code data-type="codeline"> freebsd</code><code data-type="codeline"> org</code><code data-type="codeline"> ---------------------</code><code data-type="codeline"> netbsd.org</code><code data-type="codeline"> netbsd</code><code data-type="codeline"> org</code><code data-type="codeline"> ---------------------</code>
複製程式碼
正則表示式替換字串
可以用 ReplaceAllString
替換字串。該方法返回修改後的字串。
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"regexp"
"strings"
)
func main() {
resp, err := http.Get("http://webcode.me")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
content := string(body)
re := regexp.MustCompile("<[^>]*>")
replaced := re.ReplaceAllString(content, "")
fmt.Println(strings.TrimSpace(replaced))
}
複製程式碼
該示例讀取網頁的 HTML 資料並使用正則表示式去除其 HTML 標記。
resp, err := http.Get("http://webcode.me")
複製程式碼
我們使用 http 包中的 Get 函式建立一個 GET 請求。
body, err := ioutil.ReadAll(resp.Body)
複製程式碼
我們讀取響應物件的主體。
re := regexp.MustCompile("<[^>]*>")
複製程式碼
這個模式定義了一個匹配 HTML 標籤的正則表示式。
replaced := re.ReplaceAllString(content, "")
複製程式碼
我們使用 ReplaceAllString 方法刪除所有標籤。
ReplaceAllStringFunc 函式
ReplaceAllStringFunc
返回一個字串的副本,其中正則表示式的所有匹配項都已替換為指定函式的返回值。
package main
import (
"fmt"
"regexp"
"strings"
)
func main() {
content := "an old eagle"
re := regexp.MustCompile(`[^aeiou]`)
fmt.Println(re.ReplaceAllStringFunc(content, strings.ToUpper))
}
複製程式碼
在程式碼示例中,我們將 strings.ToUpper
函式應用於字串的所有字元。
$ go run replaceallfunc.go
aN oLD eaGLe
複製程式碼
總結
模式匹配在根據基於正則表示式和語法的特定搜尋模式在字串中搜索某些字符集時起著重要作用。匹配的模式允許我們從字串中提取所需的資料並以我們喜歡的方式對其進行操作。理解和使用正則表示式是處理文字的關鍵。
在實際過程中,程式設計師會保留一組常用的正則表示式來匹配電子郵件、電話號碼等,並在需要時使用和重用它。
本文只是簡單挑選了一些常見的函式進行介紹,其實 regexp
包還有更多的函式,留給讀者自己去探索和研究。
劃線
評論
複製
- 物聯網協議的王者:MQTT
- 解密安卓微信聊天資訊儲存
- 首都線上龔巨集績:用生態賦能企業出海 實現多方共贏 | TGO 專訪
- GPT-4 都快出來了, GPT-3 的一些缺陷仍然被詬病
- 實戰監聽 Eureka client 的快取更新
- 初識 ElastricSearch
- InfoQ 2022 年趨勢報告:DevOps 與雲端計算篇
- 拒絕八股文!這篇圖解動態路由分分鐘愛了
- 從使用者走向引領者,如何加速國產開源生態建設?
- Docker 實踐經驗(二)映象的構建、映象倉庫、壓縮、匯入
- 雲原生之 Ansible 篇(一)
- ElastricSearch 第二彈之分片原理
- 超影片時代音影片架構建設與演進
- 騰訊內容結算下一代系統探索實踐
- 10 分鐘,帶你瞭解 3 篇 SIGMOD、WWW 等資料庫頂會論文的研究成果
- Java 近期新聞:NetBeans 14、Spring Tool Suite 3 支援接近尾聲、Hibernate 6.1、TornadoVM
- 打破焦慮,分析師是如何研判技術趨勢的?
- 最小可行架構實踐:構建家庭保險聊天機器人
- MobTech 楊冠軍:管理是一生的提煉,數字是一生的總結 | TGO 專訪
- 為了讓師妹 20 分鐘學會 canvas,我熬夜苦肝本文外加一個小專案【❤️建議收藏❤️】