Go 語言入門很簡單:正則表示式

語言: CN / TW / HK

前言

在計算中,我們經常需要將特定模式的字元或字元子集匹配為另一個字串中的字串。此技術用於使用特別的語法來搜尋給定字串中的特定字符集。比如郵件、手機號、身份證號等等。

如果搜尋到的模式匹配,或者在目標字串中找到給定的子集,則搜尋被稱為成功;否則被認為是不成功的。

那麼此時該用到正則表示式了。

什麼是正則表示式

正則表示式(或 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 包還有更多的函式,留給讀者自己去探索和研究。

劃線

評論

複製