Go语言

16. 正则表达式

正则表达式(Regular Expression,在代码中常简写为regex、regexp或RE),又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法,是计算机科学的一个概念。目前,很多编程语言都支持正则表达式。

16.1 正则表达式介绍

正则表达式,即符合一定规则的表达式,是用于匹配字符串中字符组合的模式。

正则表达式的设计思想就是使用一些描述性的符号和文字为字符串定义一个规则。凡是符合这个规则的,程序就认为文本是“匹配”的,否则就认为文本是“不匹配”的。

通俗地讲,正则表达式的匹配过程就是逐字匹配表达式的描述规则,如果每个字符都匹配,那么程序就认为匹配成功;只要有一个匹配不成功,那么匹配就失败。

16.2 正则表达式语法

16.2.1 普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

假设想判断一个长度为1的字符串是否是数字(即这个位置上的字符只能是“0”“1”“2”“3”“4”“5”“6”“7”“8”“9”这十个数字),如果使用程序去判断,那么一个可能的思路是用十个条件分支去判断这个字符串是否等于这十个字符。伪代码如下:

num == "0" or num == "1" or num == "2" …… or num == "9"

使用普通字符:

[0123456789]

这就是判断一个长度为1的字符串是否是数字的正则表达式。方括号“[]”表示这是一个字符组,代表一位字符。方括号中的数字“0123456789”表示只要待匹配的字符串与其中任何一个字符相同,那么程序就会认为匹配成功,反之则认为匹配失败。

更简单的写法

[0-9]

同理,如果想判断一个长度为1的字符串是不是英文小写字母:

[a-z]

所有大小写英文字母:

[A-Za-z] 或 [a-zA-Z]

如何判断一个长度为2的字符串是否是数字:

[0-9][0-9]

判断用户输入的是“Y”还是“y”:

[Yy]

判断用户输入的是“Yes”还是“yes”:

[Yy]es
16.2.2 字符转义

“[0-9]”,其中“-”表示范围,并不代表字符“-”本身,此类字符称之为元字符。不只是“-”,例子中“[”“]”都是元字符,这些字符在匹配中都有着特殊的意义。

匹配“[”本身,正则可以写成:

[\[]

匹配“0”“-”和“9”这三个字符:

[0\-9]
16.2.3 元字符

正则中常见的元字符:

元字符说明
\将下一个字符标记为特殊字符或字面值。例如,n 匹配字符 n,而 \n 匹配换行符;序列 \ 匹
配 \,而 ( 匹配 (
^匹配输入的开始部分
$匹配输入的结束部分
*零次或更多次匹配前面的字符。例如,zo* 匹配 z 或 zoo
+一次或更多次匹配前面的字符。例如,zo+ 匹配 zoo,但是不匹配 z
?零次或一次匹配前面的字符。例如,a?ve? 匹配 never 中的 ve
.匹配任何单个字符,但换行符除外
(pattern)匹配模式并记住匹配项。通过使用以下代码,匹配的子串可以检索自生成的匹配项集合:
Item [0]…[n]。要匹配圆括号字符 (),请使用 ( 或 )
x | y匹配 x 或 y。例如 z
{n}n 是一个非负整数,精确匹配 n 次。例如,o{2} 不匹配 Bob 中的 o,但是匹配 foooood 中的
前两个 o
{n,}在此表达式中,n 是一个非负整数,至少 n 次匹配前面的字符。例如,o{2,} 不匹配 Bob 中的 o,
但是匹配 foooood 中的所有 o。o{1,} 表达式等效于 o+,o{0,} 等效于 o*
{n , m}m 和 n 变量是非负整数。至少 n 次且至多 m 次匹配前面的字符。例如,o{1,3} 匹配 fooooood
中的前三个 o。o{0,1} 表达式等效于 o?
[xyz]一个字符集,匹配任意一个包含的字符。例如,[abc] 匹配 plain 中的 a
[^xyz]一个否定字符集,匹配任意未包含的字符。例如,[^abc] 匹配 plain 中的 p
[a-z]字符范围,匹配指定范围中的任意字符。例如,[a-z] 匹配英语字母中的任意小写字母字符
[^m-z]一个否定字符范围,匹配未在指定范围中的任意字符。例如,[^m-z] 匹配未在范围 m 到 z
之间的任意字符
\A仅匹配字符串的开头
\b匹配某个单词边界,即某个单词和空格之间的位置。例如,er\b 匹配 never 中的 er,但是不
匹配 verb 中的 er
\B匹配非单词边界。ea*r\B 表达式匹配 never early 中的 ear
\d匹配数字字符
\D匹配非数字字符
\f匹配换页字符
\n匹配换行符
\r匹配回车字符
\s匹配任意空白字符,包括空格、制表符、换页字符等
\S匹配任意非空白字符
\t匹配跳进字符
\v匹配垂直跳进字符
\w匹配任意单词字符,包括下划线。此表达式等效于 [A-Za-z0-9_]
\W匹配任意非单词字符。此表达式等效于 [^A-Za-z0-9_]
\z仅匹配字符串的结尾
\Z仅匹配字符串的结尾,或者结尾的换行符之前
16.2.4 限定符

限定符指定输入中必须存在字符、组或字符类的多少个实例才能找到匹配项。

  • “{n}”限定符表示匹配上一元素n次,其中n是任意非负整数。

    “y{5}”只能匹配“yyyyy”;“3{2}”则只能匹配“33”;“\w{3}”可以匹配任意三位英文字母,“yes”“Yes”“abc”和“ESC”都是可以匹配的,但是“No”“123”“No1”都不能被匹配。

  • “{n,}”限定符表示至少匹配上一元素n次,其中n是任意非负整数。

    例如,“y{3,}”可以匹配“yyy”也可以匹配“yyyyyy”。同理,“[0-9]{3,}”可以匹配任意三位数及数位为三以上的数字。

  • “{n, m}”限定符表示至少匹配上一元素n次,但不超过m次,其中n和m是非负整数。

    例如,“y{2,4}”可以匹配“yy”“yyy”和“yyyy”。同理,“[0-9]{8,11}”表示可以匹配任意八位至十一位的数字。

  • “*”限定符表示与前面的元素匹配零次或多次,它相当于“{0,}”限定符。

    例如,“91*9*”可以匹配“919”“9119”“9199999”等,但是不能匹配“9129”“929”等。

  • “+”限定符表示匹配上一元素一次或多次,它相当于“{1,}”限定符。

    例如,“an\w+”可以匹配“antrum”等以“an”开头的字母数量为三及以上的单词,但是不能匹配“an”。

  • “?”限定符表示匹配上一元素零次或一次,它相当于“{0,1}”。

    例如,“an?”可以匹配“a”和“an”,但是不能匹配“antrum”。

16.2.5 定位符

定位符能够将正则表达式固定到行首或行尾。它们还能够创建这样的正则表达式:正则表达式将出现在一个单词内、一个单词的开头或者一个单词的结尾。

位符用来描述字符串或单词的边界,“^”和“$”分别指字符串的开始与结束,“\b”描述单词的前或后边界,“\B”表示非单词边界。

字符说明
^匹配输入字符串的开始位置,如果设置了 Multiline 属性,^ 也匹配 \n 或 \r 之后的位置。除非在方
括号表达式中使用,此时它表示不接受该字符集合。要匹配字符 ^ 本身,请使用 ^
$匹配输入字符串的结尾位置。如果设置了 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配
\b匹配一个单词边界,即单词与空格间的位置
\B非单词边界匹配
  • “^”定位符指定以下模式必须从字符串的第一个字符位置开始。

    例如,“\w+”可以匹配“123abc”中的“abc”,“^\w+”则不能匹配“123abc”,但是可以匹配“abc123”中的“abc”,因为整个字符串必须是以字母开头。

  • “$”定位符指定前面的模式必须出现在输入字符串的末尾,或出现在输入字符串末尾的“\n”之前。

    例如,“\w+”可以匹配“abc123”中的“abc”,“\w+$”则不能匹配“abc123”,但是可以匹配“123abc”,因为整个字符串必须是以字母结尾。

  • “\b”定位符指定匹配必须出现在单词字符(“\w”语言元素)和非单词字符(“\W”语言元素)之间的边界上。单词字符包括字母、数字、字符和下划线;非单词字符包括不为字母、数字、字符或下划线的任意字符。匹配也可以出现在字符串开头或结尾处的单词边界上。“\b”定位符经常用于确保子表达式与整个单词(而不仅与单词的开头或结尾)匹配。

    例如,字符串“area bare arena mare”使用正则表达式“\bare\w*\b”去匹配,“area”“arena”是满足此正则表达式的。

  • “\B”定位符指定匹配不得出现在单词边界上,它与“\b”定位符截然相反。

    例如,字符串“equity queen equip acquaint quiet”使用正则表达式“\Bqu\w+”去匹配,“quity”“quip”和“quaint”是满足此正则表达式的。

16.2.6 分组构造

分组构造描述了正则表达式的子表达式,用于捕获输入字符串的子字符串。

(子表达式)

“子表达式”为任何有效正则表达式模式。使用括号的捕获按正则表达式中左括号的顺序从一开始就由左到右自动编号。

字符串“He said that that was the correct answer.”使用“(\w+)\s(\w+)\W”来匹配。

“He said”一组,其中“He”和“said”各为一个子组;
“that that”一组,其中“that”和“that”各为一个子组;
“was the”一组,其中“was”和“the”各为一个子组;
“correct answer.”一组,其中“correct”和“answer.”各为一个子组。

16.2.7 匹配模式

匹配模式指的是匹配的时候使用的规则。使用不同的匹配模式可能会改变正则表达式的识别,也可能会改变正则表达式中字符的匹配规定。

  • 不区分大小写模式指的就是在匹配单词时,正则表达式将不会区分字符串中的大小写。
  • 单行模式(也叫点号通配)会改变元字符“.”的匹配方式。元字符“.”几乎可以匹配任何字符,但是默认情况下“.”不会匹配“\n”换行符。然而,有时候确实想要匹配“任何字符”,此时单行模式就可以让“.”匹配任何字符(当然也可以使用如“[\s\S]”“[\w\W]”等技巧来匹配所有字符)。
  • 多行模式改变的是“^”和“ ”的匹配方式。默认模式下, “ ” 和“ ”的匹配方式。默认模式下,“^”和“ ”的匹配方式。默认模式下,“”和“”匹配的是整个字符串的起始位置和结束位置;但是在多行模式下,它们将会匹配字符串内部某一行文本的起始位置和结束位置。

16.3 regecp包

Go在处理正则表达式时主要使用regexp包,包中实现了正则表达式的查找、替换和模式匹配功能。

16.3.1 MatchString函数

MarchString函数接收一个要查找的正则表达式和目标字符串,并根据匹配结果返回true或false。

func MatchString(pattern string, s string) (matched bool, err error)
package main

import (
   "fmt"
   "regexp"
)

func main() {
   targetString := "hello world"
   matchString := "hello"

   match, err := regexp.MatchString(matchString, targetString)
   if err != nil {
      fmt.Println(err)
   }
   fmt.Println(match)
}

在这里插入图片描述

让字符串开头匹配时不区分大小写:

targetString := "Hello world"
matchString := "(?i)hello"

在这里插入图片描述

16.3.2 FindStringIndex函数

FindStringIndex函数接收一个目标字符串,并返回第一个匹配的起始位置和结束位置。

func (re *Regexp) FindStringIndex(s string) (loc []int)

由于FindStringIndex函数是Regexp结构体的成员函数,需要对正则表达式进行编译,编译成功后方能使用。通常使用Compile或MustCompile函数进行编译。

  • Compile函数:若正则表达式未通过编译,则返回错误。
  • MustCompile函数:若正则表达式未通过编译,则引发panic。

对于目标字符串“hello world”,通过FindStringIndex函数匹配其中的“hello”字符串对应的起始和结束位置。

package main

import (
   "fmt"
   "regexp"
)

func main() {
   targetString := "hello world"
   re := regexp.MustCompile(`(\w)+`)
   res := re.FindStringIndex(targetString)

   fmt.Println(res)
}

在这里插入图片描述

16.3.3 ReplaceAllString函数

ReplaceAllString函数返回第一个参数的拷贝,将第一个参数中所有re的匹配结果都替换为repl。

func (re *Regexp) ReplaceAllString(src, repl string) string

对于目标字符串“hello world”,通过ReplaceAllString函数匹配其中的“o”字符并将其替换为“O”

package main

import (
   "fmt"
   "regexp"
)

func main() {
   targetString := "hello world"
   re := regexp.MustCompile(`o`)
   res := re.ReplaceAllString(targetString, "O")
   fmt.Println(res)
}

在这里插入图片描述

16.5 知识拓展

常用正则表达式参考

^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([\/\w .-])\/?$
^\s|\s$
^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$
^((\d{18})|([0-9x]{18})|([0-9X]{18}))$
^(\d{3,4}-)?\d{7,8}$
^[a-zA-Z][a-zA-Z0-9_]{4,15}$
^(0?[1-9]|1[0-2])$
^\d{4}-\d{1,2}-\d{1,2}$
^((0?[1-9])|((1|2)[0-9])|30|31)$
^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9]
[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$