1.簡介
- 正則表達式,是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個“規則字符串”,這個“規則字符串”用來表達對字符串的一種匹配邏輯。從而實現以匹配邏輯為核心的一系列搜索、提取、替換等功能。
- 正則表達式已經被廣泛地應用在編程語言、編輯器、指令腳本當中,是工作中的一把利器。
-
先來系統地看一下正則表達式的語法(只列舉了筆者認為常用的部分):
正則表達式.png-31.4kB
暫時對正則表達式使用沒有概念也沒關系,后面會有詳細說明,這里先有一個大概的了解即可。
- 學習正則表達式時,有一個方便的正則語法驗證工具非常重要。這里推薦https://regexper.com,它除了可以檢查正則的語法,還能將我們的正則邏輯用圖形化的方式表達出來。比如對用一個郵箱正則
^[\w-]+@[\w-]+(\.[\w-]+)+$
,圖示如下:
email.png-8kB
2.基礎
在基礎部分,將為大家介紹如何使用字符匹配、位置匹配和次數匹配:
1.字符匹配
- 匹配任意字符 “.”
- 例如
a.c
的圖示為:
img.png-1.8kB - 也可以多個點連用,比如
a..c
:
img.png-2kB
當然,使用次數匹配(下文詳細介紹)也可以達到相同的效果,a.{2}c
:
img.png-2.7kB
- 或邏輯 “|”
- “|”默認會將兩邊所有的字符作為參數。例如
1a|b3
:
img.png-1.9kB
如果想指定參數范圍必須使用子表達式(下文詳細介紹)1(a|b)3
:
img.png-3.3kB
- 字符集合 “[]”
- “[]”可以匹配幾個特定字符中的一個。例如
1[abc]3
:
img.png-2.5kB - “[]”也可以作用在字符組區間上。例如
1[a-c]3
:
img.png-2.1kB
類似的,[a-z]表示字母a到z之間的任意一個字母,[0-9]表示0到9之間的任意一個數字。 - “[]”和“^”搭配,可以表示非字符集的匹配。例如
1[^a-c]3
:
img.png-2.1kB
即,把^放在[]里最前面的位置,表示任意一個非[]中字符的字符。
- 轉義、特定類型字符 “\”
- “\”在很多編程語言代表轉義字符,在正則表達式中也是一樣的。比如“.”是一個元字符(正則表達式中代表特殊含義的字符),表示任意一個字符。那如果想要表示“.”本身的含義,就需要寫成“\.”。例如
a\.c
專指匹配“a點c”這三個字符的字符串:
img.png-0.9kB - “\”在正則表達式中除了代表轉義,還可以與其他字母組合,表示指定類型字符。例如“\d”表示任何數字,
a\dc
:
img.png-1.4kB
將字母大寫,表示“非”的意思,例如任意一個非數字的字符,可以用“\D”表示,a\Dc
:
img.png-1.7kB
類似的有:
元字符 | 描述 |
---|---|
\d | 任何數字 (同 [0-9]) |
\D | 任何非數字 (同 [^0-9]) |
\w | 所有的文字數字式字符:大小寫字母、數字和下劃線 (同 [a-zA-Z0-9_]) |
\W | (同 [^a-zA-Z0-9_]) |
\s | 所有的空白字符 (同 [\f\n\r\t\v]) |
\S | 所有的非空白字符 (同 [^\f\n\r\t\v]) |
2.位置匹配
- 行邊界匹配的元字符是“^”和“
`:
img.png-2.2kB
3.次數匹配
- 出現0個或1個 “?”
- “?”表示一個字符可以不出現,或只出現一次。例如
ab?c
:
img.png-1.6kB - 需要注意的是,“?”是貪婪匹配的(即優先匹配字符出現次數多情況)。例如下面的Java代碼:
Pattern p=Pattern.compile("ab?");
Matcher m=p.matcher("ab111");
System.out.println("m.find(): " + m.find());
System.out.println("m.group(): " + m.group());
// 輸出
m.find(): true
m.group(): ab
- 但是“?”表示出現0個或1個,也就是說在上面的代碼中,對于“ab111”來說,“a”字符串也應該是“ab?”表達式下的匹配項。那如果才能匹配到“a”呢?這就需要用到“?”元字符的另一個含義了:懶匹配(即優先匹配字符出現次數少的情況)。
- 也就是說,“?”這個元字符比較特殊,它有兩個含義,除了在次數匹配中表示出現0個或1個外,在匹配模式上,它表示使用懶模式。
- 所以上面Java代碼的例子中,如果想要匹配到“a”這個字符串,需要將表達式改為“ab??”:
Pattern p = Pattern.compile("ab??");
Matcher m = p.matcher("ab111");
System.out.println("m.find(): " + m.find());
System.out.println("m.group(): " + m.group());
// 輸出
m.find(): true
m.group(): a
- 出現1個或更多 “+”
- “+”表示一個字符至少出現一次。例如
ab+c
:
img.png-1.6kB - 與表示0個或1個的“?”相同,"+"默認也是貪婪模式,如果想要懶模式,則要使用“+?”:
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("aaa2223bb");
System.out.println("m.find(): " + m.find());
System.out.println("m.group(): " + m.group());
p =Pattern.compile("\\d+?");
m = p.matcher("aaa2223bb");
System.out.println("m.find(): " + m.find());
System.out.println("m.group(): " + m.group());
// 輸出
m.find(): true
m.group(): 2223
m.find(): true
m.group(): 2
- 出現0個或更多 “*”
-
"*"表示一個字符可以出現任意次。例如
ab*c
:
img.png-1.9kB "*"也是貪婪模式的,用“*?”表示懶模式:
Pattern p = Pattern.compile("\\d*");
Matcher m = p.matcher("2223");
System.out.println("m.find(): " + m.find());
System.out.println("m.group(): " + m.group());
p =Pattern.compile("\\d*?");
m = p.matcher("aaa2223bb");
System.out.println("m.find(): " + m.find());
System.out.println("m.group(): " + m.group());
// 輸出
m.find(): true
m.group(): 2223
m.find(): true
m.group():
- 通用次數匹配 “{}”
- “{}”是次數匹配當中最靈活通用的,它的用法如下:
表達式 | 描述 |
---|---|
{3} | 精確次數匹配,表示一個字符連續有3個 |
{3,} | 至少次數匹配,表示一個字符至少連續有3個 |
{3,5} | 區間次數匹配,表示一個字符連續有3到5個 |
- 所以?和{0,1}的功能是一樣的,+和{1,}的作用是一樣的。
- 我們以區間次數匹配為例,看一下
ab{3,5}c
的圖示:
img.png-2.2kB
3.進階
在進階部分,將為大家介紹子表達式和后向引用:
1.子表達式
- 通過()括起來的就是子表達式。
- 為了說明子表達式的用途,先來看一個表示IPv4地址的表達式(為簡單起見,不校驗是否超過了256):
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
,圖示為:
ip.png-2.9kB - 從上面的表達式我們可以看到
\d{1,3}\.
重復了三遍,使用子表達式,可消除重復,表示為:(\d{1,3}\.){3}\d{1,3}
,圖示為:
ip2.png-5kB - 除了消除重復,子表達式還有另外一個重要的作用,那就是明確范圍。在之前將邏輯或“|”元字符的時候,曾經提過,“|”會把自己兩邊所有的字符都作為參數,如果只想要部分字符作為參數,則可以使用子表達式。看下面兩個表達式的區別:
-
19|20\d{2}
year1.png-3.1kB -
(19|20)\d{2}
year2.png-4.4kB
2.后向引用
- 后向引用就是將前面的子表達式理解成變量使用。例如\1匹配模式中第一個子表達式,\2匹配第二個子表達式,\3匹配第三個。
- 來看一個例子:
System.out.println(Pattern.compile("<H([1-6])>.*?</H\\1>").matcher("<H1>html_code</H1>").matches());
System.out.println(Pattern.compile("<H([1-6])>.*?</H\\1>").matcher("<H1>html_code</H2>").matches());
// 輸出
true
false
- 上面的Java代碼,用同一個表達式對兩段html代碼進行了匹配性檢查。第一段html代碼“<H1>html_code</H1>”檢查通過;而第二段html代碼“<H1>html_code</H2>”沒有通過,原因就是后向引用“\1”要求必須和前面的子表達式“([1-6])”內容相同。因為他們代表的是同一個變量。
end