學習正則表達式的簡單方法
本篇為
github
上一個37.4K收藏項目的學習筆記。許多程序設計語言都支持利用正則表達式進行字符串操作。例如,在Perl
建了一個功能強大的正則表達式引擎。正則表達式這個概念最初是由Unix
中的工具軟件普及開的。本篇則結合之前剛介紹過R
中的stringr
包,讓大家更加直觀的理解每個正則表達式的含義。
[TOC]
正則表達式是一種被用于從文本中檢索符合某些特定模式的文本。正則表達式是從左到右來匹配一個字符串的。正則表達式可以被用來替換字符串中的文本、驗證表單、基于模式匹配從一個字符串中提取字符串等等。
1. 基本匹配
正則表達式是用于在文本中檢索字符串的模式。例如正則表達式 cat
,表示:字母 c
后面跟著一個字母 a
,再后面跟著一個字母 t
。
library(tidyverse)
library(stringr)
x <- c("The fat cat sat on the mat.")
str_view_all(x,"cat")
正則表達式 123
會匹配字符串“123”。通過將正則表達式中的每個字符逐個與要匹配的字符串中的每個字符進行比較,來完成正則匹配。正則表達式通常區分大小寫,因此正則表達式 Cat
與字符串“cat”不匹配。
x <- c("The fat cat sat on Cat.")
str_view_all(x,"Cat")
2. 元字符
元字符是正則表達式的基本組成元素。元字符在這里跟它通常表達的意思不一樣,而是以某種特殊的含義去解釋。有些元字符在寫在方括號內時有特殊含義。
元字符如下:
元字符 | 描述 |
---|---|
. | 匹配除換行符以外的任意字符。 |
[ ] | 字符類,匹配方括號中包含的任意字符。 |
[^ ] | 否定字符類。匹配方括號中不包含的任意字符 |
* | 匹配前面的子表達式零次或多次 |
+ | 匹配前面的子表達式一次或多次 |
? | 匹配前面的子表達式零次或一次,或指明一個非貪婪限定符。 |
{n,m} | 花括號,匹配前面字符至少 n 次,但是不超過 m 次。 |
(xyz) | 字符組,按照確切的順序匹配字符 xyz。 |
| | 分支結構,匹配符號之前的字符或后面的字符。 |
\ | 轉義符,它可以還原元字符原來的含義,允許你匹配保留字符 <code>[ ] ( ) { } . * + ? ^ $ \ |</code> |
^ | 匹配行的開始 |
$ | 匹配行的結束 |
2.1 英文句號
英文句號 .
是元字符的最簡單的例子。元字符 .
可以匹配任意單個字符。它不會匹配換行符(\n
)和新行的字符。例如正則表達式 .ar
,表示:任意字符后面跟著一個字母 a
,再后面跟著一個字母 r
。
x <- c("The car parked in the garage.")
str_view_all(x,".ar")
2.2 字符集
字符集也稱為字符類。方括號被用于指定字符集。使用字符集內的連字符來指定字符范圍。方括號內的字符范圍的順序并不重要。
例如正則表達式 [Tt]he
,表示:大寫 T
或小寫 t
,后跟字母 h
,再后跟字母 e
。
x <- c("The car parked in the garage")
str_view_all(x,"[Tt]he")
然而,字符集中的英文句號表示它字面的含義。正則表達式 ar[.]
,表示小寫字母 a
,后面跟著一個字母 r
,再后面跟著一個英文句號 .
字符。
x <- c("A garage is a good place to park a car.")
str_view_all(x,"ar[.]")
2.2.1 否定字符集
一般來說插入字符 ^
表示一個字符串的開始,但是當它在方括號內出現時,它會取消字符集。例如正則表達式 [^c]ar
,表示:除了字母 c
以外的任意字符,后面跟著字符 a
,再后面跟著一個字母 r
。
x <- c("The car parked in the garage")
str_view_all(x,"[^c]ar")
2.3 重復
以下元字符 +
,*
或 ?
用于指定子模式可以出現多少次。這些元字符在不同情況下的作用不同。
2.3.1 星號
星號 *
表示匹配上一個匹配規則零次或多次。正則表達式 a*
表示小寫字母 a
可以重復零次或者多次。但是它如果出現在字符集或者字符類之后,它表示整個字符集的重復。
例如正則表達式 [a-z]*
,表示:一行中可以包含任意數量的小寫字母。
x <- c("The car parked in the garage #21.")
str_view_all(x,"[a-z]*")
星號 *
可以與元符號 .
用在一起,用來匹配任意字符串 .*
。星號 *
可以與空格符 \s
一起使用,用來匹配一串空格字符。
例如正則表達式 \s*cat\s*
,表示:零個或多個空格,后面跟小寫字母 c
,再后面跟小寫字母 a
,再在后面跟小寫字母 t
,后面再跟零個或多個空格。
x <- c("The fat cat sat on the cat.")
str_view_all(x,"\\s*cat\\s*")
#值得注意的是R中的轉義符為\\ 否則R會出現下列報錯 如果要匹配就要寫成"\\(\\)"
> str_view_all(x,"\s*cat\s*")
錯誤: 由""\s"開頭的字符串中存在'\s',但沒有這種逸出號
2.3.2 加號
加號 +
表示匹配上一個字符一次或多次。例如正則表達式 c.+t
,表示:一個小寫字母 c
,后跟任意數量的字符,后跟小寫字母 t
。
x <- c("The fat cat sat on the cat.")
str_view_all(x,"c.+t")
2.3.3 問號
在正則表達式中,元字符 ?
用來表示前一個字符是可選的。該符號匹配前一個字符零次或一次。
例如正則表達式 [T]?he
,表示:可選的大寫字母 T
,后面跟小寫字母 h
,后跟小寫字母 e
。
x <- c("The car parked in the garage")
str_view_all(x,"[T]he")
str_view_all(x,"[T]?he")
2.4 花括號
在正則表達式中花括號用于指定字符或一組字符可以重復的次數。例如正則表達式 [0-9]{2,3}
,表示:匹配至少 2 位數字但不超過 3 位(0 到 9 范圍內的字符)。
x <- c("The number was 9.9907 but we rounded it off to 10.0 .")
str_view_all(x,"[0-9]{2,3}") #注意這里的0到9是[0-9],而不是[0,9]
我們可以省略第二個數字。例如正則表達式 [0-9]{2,}
,表示:匹配 2 個或更多個數字。如果我們也刪除逗號,則正則表達式 [0-9]{2}
,表示:匹配正好為 2 位數的數字。
str_view_all(x,"[0-9]{2,}")
str_view_all(x,"[0-9]{2}")
2.5 字符組
字符組是一組寫在圓括號內的子模式 (...)
。正如我們在正則表達式中討論的那樣,如果我們把一個量詞放在一個字符之后,它會重復前一個字符。但是,如果我們把量詞放在一個字符組之后,它會重復整個字符組。
例如正則表達式 (ab)*
表示匹配零個或多個的字符串“ab”。我們還可以在字符組中使用元字符 |
。例如正則表達式 (c|g|p)ar
,表示:小寫字母 c
、g
或 p
后面跟字母 a
,后跟字母 r
。
x <- c("The car parked in the garage")
str_view_all(x,"(c|g|p)ar")
2.6 分支結構
在正則表達式中垂直條 |
用來定義分支結構,分支結構就像多個表達式之間的條件。現在你可能認為這個字符集和分支結構的工作方式一樣。但是字符集和分支結構巨大的區別是字符集只在字符級別上有作用,然而分支結構在表達式級別上依然可以使用。
例如正則表達式 (T|t)he|car
,表示:匹配大寫字母 T
或小寫字母 t
,后面跟小寫字母 h
,后跟小寫字母 e
,或匹配小寫字母 c
,后跟小寫字母 a
,后跟小寫字母 r
。
x <- c("The car parked in the garage")
str_view_all(x,"(T|t)he|car")
2.7 轉義特殊字符
正則表達式中使用反斜杠 \
來轉義下一個字符。這將允許你使用保留字符來作為匹配字符 { } [ ] / \ + * . $ ^ | ?
。在特殊字符前面加 \
,就可以使用它來做匹配字符。
例如正則表達式 .
是用來匹配除了換行符以外的任意字符。現在要在輸入字符串中匹配 .
字符,正則表達式 (f|c|m)at\.?
,表示:小寫字母 f
、c
或者 m
后跟小寫字母 a
,后跟小寫字母 t
,后跟可選的 .
字符。
x <- c("The fat cat sat on the cat.")
str_view_all(x,"(f|c|m)at\\.?")
2.8 定位符
在正則表達式中,為了檢查匹配符號是否是起始符號或結尾符號,我們使用定位符。定位符有兩種類型:
-
^
檢查匹配字符是否是起始字符。 -
$
檢查匹配字符是否是輸入字符串的最后一個字符。
2.8.1 插入符號
插入符號 ^
符號用于檢查匹配字符是否是輸入字符串的第一個字符。如果我們使用正則表達式 ^a
(如果 a 是起始符號)匹配字符串 abc
,它會匹配到 a
。
但是如果我們使用正則表達式 ^b
,它是匹配不到任何東西的,因為在字符串 abc
中“b”不是起始字符。
讓我們來看看另一個正則表達式 ^(T|t)he
,這表示:大寫字母 T
或小寫字母 t
是輸入字符串的起始符號,后面跟著小寫字母 h
,后跟小寫字母 e
。
x <- c("The car parked in the garage")
str_view_all(x,"(T|t)he")
str_view_all(x,"^(T|t)he")
2.8.2 美元符號
美元 $
符號用于檢查匹配字符是否是輸入字符串的最后一個字符。例如正則表達式 (at\.)$
,表示:小寫字母 a
,后跟小寫字母 t
,后跟一個 .
字符,且這個匹配器必須是字符串的結尾。
x <- c("The fat cat. sat. on the cat.")
str_view_all(x,"(at\\.)")
str_view_all(x,"(at\\.)$")
3. 簡寫字符集
正則表達式為常用的字符集和常用的正則表達式提供了簡寫。簡寫字符集如下:
簡寫 | 描述 |
---|---|
. | 匹配除換行符以外的任意字符 |
\w | 匹配所有字母和數字的字符:[a-zA-Z0-9_]
|
\W | 匹配非字母和數字的字符:[^\w]
|
\d | 匹配數字:[0-9]
|
\D | 匹配非數字:[^\d]
|
\s | 匹配空格符:[\t\n\f\r\p{Z}]
|
\S | 匹配非空格符:[^\s]
|
4. 斷言
后行斷言(lookbehind)和先行斷言(lookahead)有時候被稱為斷言,它們是特殊類型的 非捕獲組(用于匹配模式,但不包括在匹配列表中)。當我們在一種特定模式之前或者之后有這種模式時,會優先使用斷言。
例如我們想獲取輸入字符串 $4.44 and $10.88
中帶有前綴 $
的所有數字。我們可以使用這個正則表達式 (?<=\$)[0-9\.]*
,表示:獲取包含 .
字符且前綴為 $
的所有數字。
以下是正則表達式中使用的斷言:
符號 | 描述 |
---|---|
?= | 正向先行斷言 |
?! | 負向先行斷言 |
?<= | 正向后行斷言 |
?<! | 負向后行斷言 |
4.1 正向先行斷言
正向先行斷言認為第一部分的表達式的后面必須是先行斷言表達式。返回的匹配結果僅包含與第一部分表達式匹配的文本。要在一個括號內定義一個正向先行斷言,在括號中問號和等號是這樣使用的 (?=...)
。先行斷言表達式寫在括號中的等號后面。
例如正則表達式 (T|t)he(?=\sfat)
,表示:匹配大寫字母 T
或小寫字母 t
,后面跟字母 h
,后跟字母 e
。
在括號中,我們定義了正向先行斷言,它會引導正則表達式引擎匹配后面跟著 fat
的 The
或 the
。
x <- c("The fat cat sat on the mat.")
str_view_all(x,"(T|t)he(?=\\sfat)")
str_view_all(x,"(T|t)he(?=\\smat)")
4.2 負向先行斷言
當我們需要指定第一部分表達式的后面不跟隨某一內容時,使用負向先行斷言。負向先行斷言的定義跟我們定義的正向先行斷言一樣,唯一的區別在于我們使用否定符號 !
而不是等號 =
,例如 (?!...)
。
我們來看看下面的正則表達式 (T|t)he(?!\sfat)
,表示:從輸入字符串中獲取全部 The
或者 the
且不匹配 fat
前面加上一個空格字符。
x <- c("The fat cat sat on the mat.")
str_view_all(x,"(T|t)he(?!\\sfat)")
4.3 正向后行斷言
正向后行斷言用于獲取跟隨在特定模式之后的所有匹配內容。正向后行斷言表示為 (?<=...)
。
例如正則表達式 (?<=(T|t)he\s)(fat|mat)
,表示:從輸入字符串中獲取在單詞 The
或 the
之后的所有 fat
和 mat
單詞。
x <- c("The fat cat sat on the mat.")
str_view_all(x,"(?<=(T|t)he\\s)(fat|mat)")
4.4 負向后行斷言
負向后行斷言是用于獲取不跟隨在特定模式之后的所有匹配的內容。負向后行斷言表示為 (?<!...)
。例如正則表達式 (?<!(T|t)he\s)(cat)
,表示:在輸入字符中獲取所有不在 The
或 the
之后的所有單詞 cat
。
x <- c("The cat sat on cat.")
str_view_all(x,"(?<!(T|t)he\\s)(cat)")
5. 標記
標記也稱為修飾符,因為它會修改正則表達式的輸出。這些標志可以以任意順序或組合使用,并且是正則表達式的一部分。
標記 | 描述 |
---|---|
i | 不區分大小寫:將匹配設置為不區分大小寫。 |
g | 全局搜索:搜索整個輸入字符串中的所有匹配。 |
m | 多行匹配:會匹配輸入字符串每一行。 |
5.1 不區分大小寫
i
修飾符用于執行不區分大小寫匹配。例如正則表達式 /The/gi
,表示:大寫字母 T
,后跟小寫字母 h
,后跟字母 e
。但是在正則匹配結束時 i
標記會告訴正則表達式引擎忽略這種情況。正如你所看到的,我們還使用了 g
標記,因為我們要在整個輸入字符串中搜索匹配。
這個在R中我暫時不會實現。
<pre>
"The" => <a href="#learn-regex"><strong>The</strong></a> fat cat sat on the mat.
</pre>
<pre>
"/The/gi" => <a href="#learn-regex"><strong>The</strong></a> fat cat sat on <a href="#learn-regex"><strong>the</strong></a> mat.
</pre>
5.2 全局搜索
g
修飾符用于執行全局匹配(會查找所有匹配,不會在查找到第一個匹配時就停止)。
例如正則表達式 /.(at)/g
,表示:除換行符之外的任意字符,后跟小寫字母 a
,后跟小寫字母 t
。
因為我們在正則表達式的末尾使用了 g
標記,它會從整個輸入字符串中找到每個匹配項。
<pre>
".(at)" => The <a href="#learn-regex"><strong>fat</strong></a> cat sat on the mat.
</pre>
<pre>
"/.(at)/g" => The <a href="#learn-regex"><strong>fat</strong></a> <a href="#learn-regex"><strong>cat</strong></a> <a href="#learn-regex"><strong>sat</strong></a> on the <a href="#learn-regex"><strong>mat</strong></a>.
</pre>
5.3 多行匹配
m
修飾符被用來執行多行的匹配。正如我們前面討論過的 (^, $)
,使用定位符來檢查匹配字符是輸入字符串開始或者結束。但是我們希望每一行都使用定位符,所以我們就使用 m
修飾符。
例如正則表達式 /at(.)?$/gm
,表示:小寫字母 a
,后跟小寫字母 t
,匹配除了換行符以外任意字符零次或一次。而且因為 m
標記,現在正則表達式引擎匹配字符串中每一行的末尾。
<pre>
"/.at(.)?$/" => The fat
cat sat
on the <a href="#learn-regex"><strong>mat.</strong></a>
</pre>
<pre>
"/.at(.)?$/gm" => The <a href="#learn-regex"><strong>fat</strong></a>
cat <a href="#learn-regex"><strong>sat</strong></a>
on the <a href="#learn-regex"><strong>mat.</strong></a>
</pre>
6. 常用正則表達式
正整數:
^\d+$
負整數:
^-\d+$
電話號碼:
^+?[\d\s]{3,}$
電話代碼:
^+?[\d\s]+(?[\d\s]{10,}$
整數:
^-?\d+$
用戶名:
^[\w\d_.]{4,16}$
字母數字字符:
^[a-zA-Z0-9]*$
帶空格的字母數字字符:
^[a-zA-Z0-9 ]*$
密碼:
^(?=^.{6,}$)((?=.*[A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z]))^.*$
電子郵件:
^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})*$
IPv4 地址:
^((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))*$
小寫字母:
^([a-z])*$
大寫字母:
^([A-Z])*$
網址:
^(((http|https|ftp):\/\/)?([[a-zA-Z0-9]\-\.])+(\.)([[a-zA-Z0-9]]){2,4}([[a-zA-Z0-9]\/+=%&_\.~?\-]*))*$
VISA 信用卡號碼:
^(4[0-9]{12}(?:[0-9]{3})?)*$
日期(MM/DD/YYYY):
^(0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.](19|20)?[0-9]{2}$
日期(YYYY/MM/DD):
^(19|20)?[0-9]{2}[- /.](0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])$
-
萬事達信用卡號碼:
^(5[1-5][0-9]{14})*$
正則表達式雖然語法不多,但是卻常常化繁為簡。(說實在的,除了簡單的,我敲完基本也就忘了,常用常新,如果大家只是偶爾用到,我覺得沒必要如數家珍,用到了再去查查或者把這篇文章翻出來看看問題也不大。) 另外也有一些在線測試正則表達式的網頁,大家可以拿來練手,如: https://tool.oschina.net/regex/#。如果覺得這篇文章對你有幫助的話,希望大家點個
贊
支持一下~
Reference
[1] https://github.com/cdoco/learn-regex-zh
[2] https://github.com/ziishaned/learn-regex
License
MIT ? [Zeeshan Ahmed
往期內容