我的GitHub | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|
baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目錄
正則表達式
學習資料
- GitHub 高星入門教程:learn-regex
- 正則表達式30分鍾入門教程:網站、博客
- Microsoft文檔:快速參考、微軟的.NET正則表達式教程
- 書籍《精通正則表達式》:圖書介紹,PDF版下載
- 其他:菜鳥教程、專業的正則表達式教學網站(英文)
實用工具
- 強大的在線工具:regex101、regexr
- 小巧的在線工具:regexpal、Rubular
- 最強大的工具 RegexBuddy:官網、v4.11破解版下載
- 國產工具 Regester(基於.Net):官網、v2.0版下載
- 其他:正則表達式引擎/風味對比
一些案例
一些案例:
\bhi\b.*\bLucy\b
:先是一個單詞hi
,然后是任意個任意字符(但不能是換行),最后是Lucy
這個單詞0\d{2}-\d{8}
:以0
開頭,然后是2個數字,然后是一個連字號-
,最后是8個數字(電話號碼)\ba\w*\b
:匹配以字母a
開頭的單詞^\d{5,12}$
:匹配整個字符串必須是5到12個數字C:\\\\Windows\\bqt\.com
:精准匹配C:\\Windows\bqt.com
0\d{2}-\d{8}|0\d{3}-\d{7}
:匹配兩種以連字號分隔的電話號碼,一種是3位區號+8位本地號(如010-12345678),一種是4位區號+7位本地號(如0376-2233445)S+
:匹配不包含空白符的字符串(以空白符分割所有單詞、數字、漢字等內容)<a[^>]+>
:匹配用尖括號<>
括起來的以a
開頭的字符串
一個復雜的案例
\(?0\d{2}[) -]?\d{8}
:匹配幾種格式的電話號碼
- 首先是一個轉義字符
\
,表示后面的(
會被當做普通的左小括號來處理 - 然后元字符
?
代表(
能出現0次或1次 - 然后是一個
0
,后面跟着2個數字\d{2}
- 然后是一個字符集
[) -]
,代表后面會出現)
或-
或空格
中的一個,它能出現0次或1次 - 最后
\d{8}
代表8個數字
然而,上面那個表達式也能匹配010)12345678
或(022-87654321
這樣的不正確的格式。要解決這個問題,就需要用到分枝條件。
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}
:這個表達式匹配3位區號的電話號碼,其中區號可以用小括號括起來,也可以不用;區號與本地號間可以用連字號或空格間隔,也可以沒有間隔。
元字符
元字符(MetaCharacter)不代表他們本身的字面意思,他們都有特殊的含義
。注意,一些元字符寫在方括號
中的時候另有一些特殊的意思。
例如元字符\b
代表着單詞的開頭或結尾,也就是單詞的分界處。
雖然通常英文的單詞是由空格,標點符號或者換行來分隔的,但是
\b
並不匹配這些單詞分隔字符中的任何一個,它只匹配一個位置(意思是說,匹配結果中不包含\b
)。
\b
:assert position at a word boundary:(^\w|\w$|\W\w|\w\W)
聲明單詞邊界處的位置(不包括這些邊界處的分割符),可以匹配以下位置:
^\w
:以\w
開頭的位置\w$
:以\w
結尾的位置\W\w
:非\w
+\w
的位置\w\W
:\w
+非\w
的位置
常用的元字符
以下是正則表達式常用的元字符
的介紹:
元字符 | 描述 |
---|---|
. | 匹配任意單個字符,除了換行符 |
[ ] |
字符種類。匹配方括號內的任意字符 |
[^ ] |
否定的字符種類。匹配除了方括號里的任意字符 |
* | 匹配>=0 個重復的在* 號之前的字符(任意次) |
+ | 匹配>=1 個重復的+ 號前的字符(至少一次) |
? | 標記? 之前的字符為可選(最多一次) |
{n,m} | 匹配[n,m] 個大括號之前的字符或字符集 |
(xyz) | 字符集,匹配與 xyz 完全相等的字符串 |
| | 或運算符,匹配符號前或后的字符 |
\ | 轉義字符,用於匹配一些保留的字符 |
\num | 獲取第 num 個分組匹配的文本 |
^ | 匹配字符串的開頭 |
$ | 匹配字符串的結尾 |
保留字符有:[ ] ( ) { } . * + ? ^ $ \ |
常用的字符集簡寫
正則表達式提供一些常用的字符集簡寫
。如下:
簡寫 | 描述 |
---|---|
\w | 匹配所有字母、數字、下划線,等同於 [a-zA-Z0-9_] |
\W | 匹配所有非字母數字、下划線,等同於: [^\w] |
\b | 匹配單詞邊界,在字符類里使用代表退格 |
\B | 匹配非單詞邊界,等同於: [^\b] |
\d | 匹配數字,等同於: [0-9] |
\D | 匹配非數字,等同於: [^\d] |
\s | 匹配所有空格字符,等同於: [\t\n\f\r\p{Z}] |
\S | 匹配所有非空格字符,等同於: [^\s] |
. | 匹配除換行符外的所有字符,等同於:[^\n] |
\n | 匹配一個換行符 |
\f | 匹配一個換頁符 |
\r | 匹配一個回車符 |
\t | 匹配一個制表符,Tab |
\v | 匹配一個垂直制表符 |
\p | 匹配 CR/LF,等同於 \r\n ,用來匹配 DOS 行終止符 |
\e | Escape |
\a | 報警字符,打印它的效果是電腦嘀一聲 |
\A | 字符串開頭,類似^,但不受處理多行選項的影響 |
\z | 字符串結尾,類似$,但不受處理多行選項的影響 |
\Z | 字符串結尾或行尾,不受處理多行選項的影響 |
基礎語法
正則表達式到底是什么東西?在處理字符串時,經常會有查找符合某些復雜規則的字符串的需要。正則表達式就是用於描述這些規則的工具。換句話說,正則表達式就是記錄文本規則的代碼。
正則表達式是一組由
字母、數字和符號
組成的特殊文本,它可以用來從文本中找出滿足你想要的格式的句子。
Regular expression
這個詞比較拗口,我們常使用縮寫的術語regex
或regexp
。
基本匹配
正則表達式默認是大小寫敏感
的。
表達式 `the` 表示一個規則:由字母`t`開始,接着是`h`,再接着是`e` "the" => The fat cat sat on the mat.
點運算符 .
.
匹配任意單個字符
,但不匹配換行符
。
表達式 `.ar` 匹配一個任意字符后面跟着是`ar`的字符串 ".ar" => The car parked in the garage.
字符集 []
- 方括號用來指定一個
字符集
(也叫做字符類
),表達式[ab]
表示匹配一個"a"或一個"b" - 在方括號中使用
連字符-
來指定字符集的范圍,例如表達式[a-dg]
相當於[abcdg]
或者a|b|c|d|g
- 方括號表示一個字符集中的字符允許在一個字符串中的某一特定位置出現
- 方括號中的字符集不關心順序
- 方括號中的保留字符
不需要轉義
表達式 `[Tt]he` 匹配 `the` 和 `The` "[Tt]he" => The car parked in the garage. 表達式 `ar[.]` 僅匹配 `ar.`字符串 "ar[.]" => A garage is a good place to park a car.
否定字符集 ^
一般來說 ^
表示一個字符串的開頭,但它用在一個方括號內部的開頭
的時候,它表示這個字符集是否定的。
例如:[aeiou]
匹配任何一個英文元音字母,[^aeiou]
匹配除了aeiou
這幾個字母以外的任意字符
表達式 `[^c]ar` 匹配一個后面跟着`ar`的除了`c`的任意字符 "[^c]ar" => The car parked in the ggarage.
重復次數
后面跟着元字符 +
,*
,?
或{}
的,用來指定匹配子模式的次數。
*
號
*
號匹配 在*
之前的字符出現>=0
次,相當於{0,}
*
字符和.
字符搭配可以匹配所有的字符.*
(非常重要)*
可以和表示匹配空格的符號\s
連起來用
表達式`\s*cat\s*`匹配0或更多個空格開頭和0或更多個空格結尾的cat字符串 "\s*cat\s*" => The fat cat sat on the concatenation.
+
號
+
號匹配+
號之前的字符出現 >=1 次,相當於{1,}
表達式 `c.+t` 匹配以首字母`c`開頭以`t`結尾,中間跟着至少一個字符的字符串 "c.+t" => The fat cat sat on the mat.
注意:以上匹配結果只有一個,可以通過 regex debugger 查看匹配過程
?
號
?
號匹配?
號之前的字符出現 0 或 1 次,相當於{0,1}
表達式 `[T]?he` 匹配字符串 `he` 和 `The` "[T]?he" => The car is parked in the garage. "[T]he" => The car is parked in the garage.
{}
號
在正則表達式中 {}
是一個量詞,常用來限定一個或一組字符
可以重復出現的次數。
表達式 `[0-9]{2,3}` 匹配最少 2 位最多 3 位 0~9 的數字 "[0-9]{2,3}" => The number was 9.9997 but we rounded it off to 10.0. 表達式 `[0-9]{2,}` 匹配至少兩位 0~9 的數字 "[0-9]{2,}" => The number was 9.9997 but we rounded it off to 10.0. 表達式 `[0-9]{3}` 匹配固定3位數字 "[0-9]{3}" => The number was 9.9997 but we rounded it off to 10.0.
或運算符 |
或運算符(分枝條件)表示或,用作判斷條件。
表達式 `(T|t)he|car` 匹配 `(T|t)he` 或 `car` "(T|t)he|car" => The car is parked in the garage.
使用或運算符時,要注意各個條件的順序。
例如,表達式
\d{5}-\d{4}|\d{5}
用於匹配美國的郵政編碼。美國郵編的規則是5位數字,或者用連字號間隔的9位數字。
如果你把它改成\d{5}|\d{5}-\d{4}
的話,那么就只會匹配5位的郵編(以及9位郵編的前5位)。
原因是匹配分枝條件時,將會從左到右
地測試每個條件,如果滿足了某個分枝的話,就不會去再管其它的條件了。
轉義字符 \
反斜線 \
在表達式中用於轉碼緊跟其后的字符。用於指定 { } [ ] / \ + * . $ ^ | ?
這些特殊字符。如果想要匹配這些特殊字符則要在其前面加上反斜線 \
。
表達式 `\.?` 表示選擇性匹配字符`.` "(f|c|m)at\.?" => The fat cat sat on the mat.
錨點 ^
和 $
這兩個元字符常用來校驗電話、IP等信息,很少用來檢索或替換,因為其條件太苛刻,功能類似於 startsWith/endsWith 的增強版,只不過startsWith/endsWith 並不支持正則表達式
在正則表達式中,想要匹配指定開頭或結尾的字符串就要使用到錨點。^
指定開頭,$
指定結尾。
^
用來檢查匹配的字符串是否在所匹配字符串的開頭。
例如,在 abc
中使用表達式 ^a
會得到結果 a
,但如果使用 ^b
將匹配不到任何結果,因為在字符串 abc
中並不是以 b
開頭。
表達式 `^(T|t)he` 匹配以 `The` 或 `the` 開頭的字符串 "^(T|t)he" => The car is parked in the garage. "(T|t)he" => The car is parked in the garage.
同理,$
號用來匹配字符是否是最后一個。
表達式 `(at\.)$` 匹配以 `at.` 結尾的字符串 "(at\.)$" => The fat cat. sat. on the mat. "(at\.)" => The fat cat. sat. on the mat.
修正符 標志(Flags)
標志也叫模式修正符,因為它可以用來修改表達式的搜索結果。這些標志可以任意的組合使用
,它也是整個正則表達式的一部分。
除了全局搜索外(最基本的功能,必須支持),常用的修正符一般都有兩種用法:
-
將修正符單獨作為一個參數,如 Java 中的 Pattern 類就需要提供了一個
flags
參數(默認為0),此值需要使用 Pattern 類中定義的常量,例如Pattern.CASE_INSENSITIVE
-
將修正符集成到正則表達式中,例如,將
(?i)
放在正則表達式的前面,和使用Pattern.CASE_INSENSITIVE
的效果是一樣的(?i)
:聲明此后的正則表達式不區分大小寫(?imsU)
:聲明此后的正則表達式同時滿足i、m、s、U等所有模式(?x) #
:聲明此后的內容為注釋,其他一般是放在正則前面,而他要放在最后
public static Pattern compile(String regex) {
return new Pattern(regex, 0);
}
public static Pattern compile(String regex, int flags) {
return new Pattern(regex, flags);
}
//This private constructor is used to create all Patterns.
//The pattern string and match flags are all that is needed to completely describe a Pattern.
private Pattern(String p, int f) {...}
全局搜索 g
修飾符 g
常用於執行一個全局搜索匹配,即不僅僅返回第一個匹配的,而是返回全部。
global. All matches (don't return after first match)
"/.(at)/g" => The fat cat sat on the mat. "/.(at)/" => The fat cat sat on the mat.
一般情況下,IDE、正則匹配工具、編程語言默認都是全局模式,除非手動關閉。
Matcher m = Pattern.compile(".(at)").matcher("The fat cat sat on the mat.");
while (m.find()) {
System.out.println(m.group()); //fat、cat、sat、mat
}
忽略大小寫 i
修飾語 i
用於忽略大小寫。
insensitive. Case insensitive match (ignores case of [a-zA-Z])
Case-insensitive matching can also be enabled via the embedded flag expression
(?i)
"/The/gi" 或 "/(?i)The/g" => The fat cat sat on the mat. "The" => The fat cat sat on the mat.
多行模式 m
多行修飾符 m
常用於執行一個多行匹配。
multi line. Causes
^
and$
to match the begin/end of each line (not only begin/end of string)
Multiline mode can also be enabled via the embedded flag expression
(?m)
元字符 ^
$
用於檢查格式是否是在待檢測字符串的開頭或結尾,但我們如果想要它在每行的開頭和結尾生效,我們需要用到多行修飾符 m
。
"/.at(.)?$/g" => The fat cat sat on the mat. "/.at(.)?$/gm" 或 "/(?m).at(.)?$/g" => The fat cat sat on the mat.
//以下兩個結果一致
Matcher m = Pattern.compile("(?m).at(.)?$").matcher("The fat \ncat sat \non the mat");
Matcher m = Pattern.compile(".at(.)?$",Pattern.MULTILINE).matcher("The fat \ncat sat \non the mat");
單行模式 s
如果沒有使用這個標志,元字符中的.
默認不能表示換行符號
;使用此標志后,元字符.
也可以表示換行符號
,所以效果就是在使用.
時將字符串視為單行
。
single line. Dot (
.
) will match any character, including newline.
Dotall mode(single-line mode) can also be enabled via the embedded flag expression
(?s)
例如,對於字符串
bqt
qt
有以下幾個測試 Case:
- 正則表達式
/.qt/g
只可以匹配到bqt
- 正則表達式
/.qt/gs
或/(?s).qt/g
可以匹配到bqt
和\nqt
(\n
表示一個換行符) - 正則表達式
/.*qt/g
可以匹配到bqt
和qt
(前面沒有換行符) - 正則表達式
/.*qt/gs
或/(?s).*qt/g
可以匹配到整個結果(默認啟用貪婪匹配導致的) - 正則表達式
/.*qt/gsU
可以匹配到bqt
和\nqt
(啟用惰性匹配)
一個經常被問到的問題是:是不是只能同時使用
多行模式
和單行模式
中的一種?
答案是:不是。
這兩個選項之間沒有任何關系,除了它們的名字比較相似(以至於讓人感到疑惑)以外。事實上,為了避免混淆,在 JavaScript、Java 中單行模式分別叫dotAll
、DOTALL
。
惰性匹配 U
?
正則表達式默認采用貪婪匹配
模式,在該模式下會匹配盡可能長
的子串。
有時,我們更需要懶惰匹配
,也就是匹配盡可能少
的字符。我們可以使用 U
或?
將貪婪匹配模式轉化為惰性匹配
模式。
例如:如果用
a.*b
來搜索aabab
的話,它會匹配整個字符串aabab
如果用a.*?b
來搜索aabab
的話,它會匹配aab[1,3]
和ab[4,5]
為什么第一個匹配是
aab[1,3]
而不是ab[2,3]
?簡單地說,因為正則表達式有另一條比懶惰/貪婪優先級更高的規則:最先開始的匹配擁有最高的優先權
——The match that begins earliest wins。
代碼/語法 | 說明 |
---|---|
*? |
重復任意次,但盡可能少重復 |
+? |
重復1次或更多次,但盡可能少重復 |
?? |
重復0次或1次,但盡可能少重復 |
{n,m}? |
重復n到m次,但盡可能少重復 |
{n,}? |
重復n次以上,但盡可能少重復 |
Ungreedy. The match becomes lazy by default. Quantifiers followed by
?
will become greedy.
個人猜測(自測也OK) Ungreedy mode can also be enabled via the embedded flag expression
(?U)
"/(.*at)/" => The fat cat sat on the mat. "/(.*?at)/" => The fat cat sat on the mat.
親測后得出的結論:
/(.*at)/U
能用/(?U)(.*at)/
替換是天經地義的(和上面幾種一樣)- 上面這兩種也能用
/(.*?at)/
替換,都具有轉化為惰性匹配
模式的效果 - 但是,上面這兩種和
/(.*?at)/
不能同時使用,否則將沒任何效果(負負得正)
擴展(注釋) (?x) #comment
如果設定了此修正符,模式中的空白字符
除了被轉義
的或在字符類
中的以外完全被忽略。
extended. Literal whitespace characters are ignored, except in character sets.
例如:
- 正則表達式
/b qt/
可以匹配字符串b qt
(正常模式) - 正則表達式
/b qt/x
不能匹配字符串b qt
(忽略空白字符) - 正則表達式
/b\ qt/x
可以匹配字符串b qt
(轉義字符) - 正則表達式
/b\sqt/x
可以匹配字符串b qt
(字符類)
這個修正符真正有用的地方在於:在#
以及下一個換行符
之間的所有字符,包括兩頭,都被忽略,這使得可以在復雜的模式中加入注釋
。
Permits允許 whitespace and comments注釋 in pattern.
In this mode, whitespace is ignored, and embedded嵌入的 comments starting with
#
are ignored until the end of a line.
Comments mode can also be enabled via the embedded flag expression
(?x)
.
例如:
- 正則表達式
/bqt #這是注釋/gx
和可以匹配bqt
,不可以匹配b qt
- 正則表達式
/b qt #這是注釋/gx
同樣也是可以匹配bqt
,不可以匹配b qt
- 正則表達式
/b qt(?x) #這是注釋/g
可以匹配b qt
,不可以匹配bqt
//在Java中,以下3個 Pattern 的效果是一樣的
Pattern.compile(".at")
Pattern.compile(".at(?x) #這是注釋")
Pattern.compile(".at #這是注釋", Pattern.COMMENTS)
分組 ()
如果想要重復多個字符,可以用小括號來指定子表達式
(也叫做分組
),然后你就可以指定這個子表達式的重復次數了。
例如:
- 表達式
ab+
或ab{1,}
匹配一個a
后連續出現 1 個或更多個b
- 表達式
(ab)+
或(ab){1,}
匹配連續出現 1 個或更多個ab
表達式 `(c|g|p)ar` 匹配 `car` 或 `gar` 或 `par` "(c|g|p)ar" => The car is parked in the garage.
分組使用案例
使用分組的一個最簡單的原因是:可以將很長的、復雜的正則表達式分割成多個子表達式
,以方便理解。
例如,(\d{1,3}\.){3}\d{1,3}
是一個簡單的IP地址匹配表達式:\d{1,3}\.
匹配1到3位的數字加上一個英文句號,(){3}
表示括號中的這個整體(也就是這個分組)重復3次,最后再加上一個1到3位的數字\d{1,3}
。
然而,上面的表達式也將匹配
256.300.888.999
這種不可能存在的IP地址。如果能使用算術比較的話,或許能簡單地解決這個問題,但是正則表達式中並不提供關於數學的任何功能,所以只能使用冗長的分組、選擇、字符類
來描述一個正確的IP地址:((A)\.){3}(A)
((A)\.){3}
代表前面有 3 個A.
(A)
代表后面有 1 個A
其中,上面的A代表
2[0-4]\d|25[0-5]|[01]?\d\d?
(不能用后向引用):
2[0-4]\d
代表可以為 200 - 249 之間的所有數字25[0-5]
代表可以為 250- 255 之間的所有數字[01]?\d\d?
代表可以為 0 - 199 之間的所有數字(05、08之類的也是合法的)
常用分組語法
使用小括號的時候,還有很多特定用途的語法。下面列出了最常用的一些:
分類 | 代碼/語法 | 說明 |
---|---|---|
捕獲 | (exp) | 匹配exp,並捕獲文本到自動命名的組里 |
捕獲 | (?<name>exp) | 匹配exp,並捕獲文本到名稱為name的組里,尖括號也可以寫成單引號 |
沒啥卵用 | (?:exp) | 匹配exp,不捕獲匹配的文本,也不給此分組分配組號 |
零寬斷言 | (?=exp) | 匹配exp前面的位置 |
零寬斷言 | (?<=exp) | 匹配exp后面的位置 |
零寬斷言 | (?!exp) | 匹配后面跟的不是exp的位置 |
零寬斷言 | (?<!exp) | 匹配前面不是exp的位置 |
注釋 | (?#comment) | 用於提供注釋讓人閱讀 |
后向引用
使用小括號指定一個子表達式后,匹配這個子表達式的文本(也就是此分組捕獲的內容)可以在表達式或其它程序中作進一步的處理。
后向引用
用於重復搜索前面某個分組匹配的文本。
組號 \1
默認情況下,每個分組會自動擁有一個組號
,組號分配的規則是:
- 從左向右,以分組的左括號為標志,第一個出現的分組的組號為1,第二個為2,以此類推
- 分組
0
對應整個正則表達式 - 可以使用
(?:exp)
這樣的語法來剝奪一個分組對組號分配的參與權 - 實際上組號分配過程是要從左向右掃描兩遍:第一遍只給
未命名組
分配,第二遍只給命名組
分配,因此所有命名組的組號都大於未命名的組號
例如,對於正則表達式((A)(B(C)))
,其中有四個組,編號1為((A)(B(C)))
,編號2為(A)
,編號3為(B(C))
,編號4為(C)
案例1,正則表達式(.)\1{2,}
可以用來匹配連續出現多次的字符:
- 首先,需要先匹配任意一個字符
(.)
,其中.
表示匹配除換行符外的任意字符 - 然后,根據組號分配規則,這個單詞會被捕獲到編號為
1
的分組中,其中\1
代表分組1
匹配的文本 - 最后,需要連續出現前面
分組1
中捕獲的內容,其中{2,}
代表分組1
匹配的文本又連續出現了至少2次
案例2,正則表達式\b(\w+)\b\s+\1\b
可以用來匹配重復的單詞,像go go
, 或者kitty kitty
:
- 首先,需要先匹配一個單詞
\b(\w+)\b
,其中\w
表示匹配所有字母、數字、下划線 - 然后,根據組號分配規則,這個單詞會被捕獲到編號為
1
的分組中 - 然后,需要出現1個或多個空白符
\s+
- 最后,需要再出現一次前面
分組1
中捕獲的內容,其中\1
代表分組1
匹配的文本
Java 中可以通過調用 matcher 對象的
groupCount()
方法來查看表達式有多少個分組,但是代表整個表達式的 group(0) 不包括 在groupCount()
的返回值中。
命名組 (?<name>exp)
可以使用(?<name>exp)
或者(?'name'exp)
指定一個子表達式的組名
,這樣,子表達式exp
就是一個命名組
了,且其組名為name
。
命名后,就可以使用\k<name>
反向引用這個分組捕獲的內容。
使用命名組反向引用分組的方式,可以避免通過組號引用分組時因計算分組錯誤導致引用出錯的情況。
所以上一個例子也可以寫成這樣:\b(?<Word>\w+)\b\s+\k<Word>\b
。
零寬斷言
斷言用來聲明一個應該為真的事實。正則表達式中只有當斷言為真時才會繼續進行匹配。
下面的四個用於查找在某些內容之前或之后
的東西(但並不包括這些內容),也就是說它們像\b
^
$
那樣用於指定一個位置
,這個位置應該滿足一定的條件(即斷言),因此它們也被稱為零寬斷言
。
他們都都屬於非捕獲簇(不捕獲文本 ,也不針對組合計進行計數)。
先行斷言用於判斷所匹配的格式是否在另一個確定的格式之前,匹配結果不包含該確定格式(僅作為約束)。
例如,我們想要獲得所有跟在 $
符號后的數字,我們可以使用正后發斷言 (?<=\$)[0-9\.]*
。
這個表達式匹配 $
開頭,之后跟着 0,1,2,3,4,5,6,7,8,9,.
這些字符可以出現大於等於 0 次。
零寬度斷言如下:
符號 | 描述 |
---|---|
?= |
正先行斷言-存在 |
?! |
負先行斷言-排除 |
?<= |
正后發斷言-存在 |
?<! |
負后發斷言-排除 |
正先行斷言 (?=exp)
正先行斷言(也叫零寬度正預測先行斷言
),它斷言自身出現的位置的后面能匹配
表達式exp,返回結果只包含滿足匹配條件的第一部分
表達式。
例如,表達式 (T|t)he(?=\sfat)
:
- 首先
(T|t)he
表示匹配The
和the
- 然后我們又定義了正先行斷言
(?=\sfat)
,其中\s
表示匹配所有空格字符 - 所以表達式整體表示:匹配所有
(T|t)he
、且其后面緊跟着(空格)fat
的內容,並返回匹配的(T|t)he
表達式 `(T|t)he(?=\sfat)` 匹配所有 `(T|t)he`,且其后跟着 `(空格)fat` "(T|t)he(?=\sfat)" => The fat cat sat on the mat. 表達式 `(T|t)he(?=\s.at)` 匹配所有 `(T|t)he`,且其后跟着 `(空格)(任意字符)at` "(T|t)he(?=\s.at)" => The fat cat sat on the mat. 表達式 `\b\w+(?=ing\b)` 匹配以 `ing` 結尾的單詞的前面部分 "\b\w+(?=ing\b)" => I'm singing while you're dancing.
負先行斷言 (?!exp)
負先行斷言(也叫零寬度負預測先行斷言
),定義和正先行斷言一樣,區別就是=
替換成!
,它斷言此位置的后面不能匹配
表達式exp。
表達式 `(T|t)he(?!\sfat)` 匹配所有 `(T|t)he`,且其后不跟着 `(空格)fat` "(T|t)he(?!\sfat)" => The fat cat sat on the mat.
正后發斷言 (?<=exp)
正后發斷言(也叫零寬度正回顧后發斷言
),它斷言自身出現的位置的前面能匹配
表達式exp。
表達式 `(?<=(T|t)he\s)(fat|mat)` 匹配所有 (fat|mat),且其前跟着 `The(空格)` 或 `the(空格)` "(?<=(T|t)he\s)(fat|mat)" => The fat cat sat on the mat. 表達式 `(?<=\bre)\w+\b` 會匹配以`re`開頭的單詞的后半部分 "(?<=\bre)\w+\b" => reading a book
表達式
(?<=\s)\d+(?=\s)
匹配以空白符間隔的數字(不包括這些空白符)。
負后發斷言 (?<!exp)
負后發斷言(也叫零寬度負回顧后發斷言
),定義和正后發斷言一樣,區別就是=
替換成!
,它斷言此位置的前面不能匹配
表達式exp。
表達式 `(?<!(T|t)he\s)(cat)` 匹配 `cat`,且其前不跟着 `The(空格)` 或 `the(空格)` "(?<!(T|t)he\s)(cat)" => The cat sat on cat.
匹配不包含屬性的簡單 HTML 標簽內里的內容
:(?<=<(\w+)>).*(?=<\/\1>)
- 首先
(?<=<(\w+)>)
指定了<(\w+)>
的前綴:被尖括號括起來的單詞,比如可能是<b>
- 然后是任意的字符串
.*
- 最后
(?=<\/\1>)
指定了<\/\1>
的后綴:- 后綴里的
\/
用到了字符轉義,表示的就是一個/
\1
是一個反向引用,引用的正是前面的(\w+)
匹配的內容- 這樣如果前綴實際上是
<b>
的話,后綴就是</b>
了
- 后綴里的
- 整個表達式匹配的是
<b>
和</b>
之間的內容(不包括前綴和后綴本身)
注釋 (?#comment)
小括號的另一種用途是通過語法(?#comment)
來包含注釋。
例如:
2[0-4]\d(?#匹配200-249)|25[0-5](?#匹配250-255)|[01]?\d\d?(?#匹配0-199)
。
要包含注釋的話,最好是啟用忽略模式里的空白符選項
,這樣在編寫表達式時能任意的添加空格、Tab、換行
,而實際使用時這些都將被忽略。啟用這個選項后,在#
后面到這一行結束的所有文本都將被當成注釋忽略掉。
例如,我們可以把前面的表達式(?<=<(\w+)>).*(?=<\/\1>)
寫成這樣:
(?<= # 斷言要匹配的文本的前綴
<(\w+)> # 查找尖括號括起來的內容,(即HTML/XML標簽),比如可能是<b>
) # 前綴結束
.* # 匹配任意文本
(?= # 斷言要匹配的文本的后綴
<\/\1> # 查找尖括號括起來的內容,如果前綴實際上是<b>的話,后綴就是</b>
) # 后綴結束
2020-03-30