關於正則表達式

shanzm-2019年5月1日 00:07:35

1.簡介

1.1 概念

正則表達式:使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。這個實際的正則表達式,也稱爲:模式(pattern)正則表達式

1.2 具體用處

  • 【查找】:一些軟件中自帶的搜索功能,是支持正則表達式的,能夠實現有目的的高效率的快速搜索目標文件
    如:Office軟件,VS,通常的編程用的文本編輯器,Everything編程

  • 【替換】:批量提取/替換有規律的字符串,如網絡爬蟲和模板引擎的標籤庫的開發數組

  • 【檢驗】:各類開發語言中的使用,處理文本和大字符串,同時能夠對用戶輸入的合法性驗證(IP地址,特殊的訂單號要求等)markdown




2.具體的語法

2.1 經常使用的元字符

【匹配字符】:網絡

元字符 說明
. 匹配除換行符之外的任意字符
\w 匹配字母或數字或下劃線或漢字
\s 匹配任意的空白符
\d 匹配數字

【匹配位置(邊界)】:app

元字符 說明
\b 匹配單詞的開始或結束
^ 匹配字符串的開始
$ 匹配字符串的結束

【說明】less

  • 一個元字符只匹配一個字符,如何匹配幾個字符(即:字符串),要使用「重複匹配」符號。編輯器

  • 位置元字符,只是表明位置,不匹配任何字符ide




2.2 反義元字符

語法 說明
\W 匹配任意不是字母,數字,下劃線,漢字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非數字的字符
\B 匹配不是單詞開頭或結束的位置
[^x] 匹配除了x之外的任意字符
[^aeiou] 匹配除了aeiou這幾個字母之外的任意字符




2.3 轉義

元字符是一些在正則表達式裏有着特殊含義的字符。

由於元字符在正則表達式裏有着特殊的含義,因此這些字符就沒法用來表明它們自己。

故須要對一些特殊字符進行轉義,使用 反斜槓「」 轉義。

這一點和C#語言中語法,甚至markdown語法都同樣,不贅述。
eg.

「\.」    匹配「.」 
「\* 」   匹配「*」
「\?」    匹配「?」
「\[」    匹配「["
「\\」    匹配「\"




2.4 註釋

(?# 註釋內容)




2.5 重複匹配

語法 說明
* 重複零次或更屢次
+ 重複一次或更屢次
? 重複零次或一次 =={0,1}
{n} 重複n次
{n,} 重複n次或更屢次
{n,m} 重複nm次 (不是n或m次)




2.6 字符集合

【定義】 使用方括號「[ ]」表示一個集合,使用連字符「-」表示從某某到某某

【注意】"-"(連字符)是一個特殊的元字符,做爲元字符它只能用在[和]之間。在字符集合之外的地方,一隻是一個普通字符,只能與一自己相匹配。所以,在正則表達式裏,一字符不須要被轉義。

【例子】

[A-Z]匹配從A到z的全部大寫字母。
[a-z]匹配從a到z的全部小寫字母。
[A-F]匹配從A到F的全部大寫字母。
[abcd]匹配a或b或c或d
[0-9] == \d
[a-z0-9A-Z_]  == \w

【字符集取非】
字符集合一般用來指定一組必須匹配其中之一的字符。但在某些場合,咱們須要反過來作,給出一組不須要獲得的字符。換句話說:除了那個字符集合裏的字符,其餘字符均可以匹配。
使用用元字符「^」來對字符集合求非。

【例子】
[0-9]匹配的是任何不是數字的字符




2.7 貪婪和懶惰

【定義】貪婪,即過分匹配。
*和+都是所謂的「貪婪型」元子符,它們在進行匹配時的行爲模式是多多益善而不是適可而止的。
它們會盡量地從一段文本的開頭一直匹配到這段文本的末尾,而不是從這段文本的開頭匹配到碰到第一個匹配時爲止。

正則表達式的做用就時快速準確的找到你想要的字符串,因此咱們更喜歡「懶惰」。

在不須要這種「貪婪行爲」的時候該怎麼辦?
答案是使用這些元字符的「懶惰型」版本(「懶惰」在這裏的含義是匹配儘量少的字符,與「貪婪型」元字符的行爲模式恰好相反)。

【語法】懶惰型元字符的寫法很簡單,只要給貪婪型元字符加上一個?後綴便可。

經常使用的貪婪型元字符的懶惰型版本

語法 說明
*? 重複任意次,但儘量少重複
+? 重複1次或更屢次,但儘量少重複
?? 重複0次或1次,但儘量少重複
{n,m}? 重複n到m次,但儘量少重複
{n,}? 重複n次以上,但儘量少重複

【例1】:

字符串:
This offer is not available to customers living in <B>AK</B>and <B>HI</B>.

貪婪型正則表達式:<B>.*</B>
結果(匹配到一個符合的字符串):<B>AK</B>and <B>HI</B>

懶惰型正則表達式:<B>.*?</B>
結果(匹配到兩個符合的字符串): <B>AK</B>
                             <B>AK</B>

【例2】:

字符串:aaabbb
貪婪正則表達式:a.*b
結果:aaabbb

懶惰正則表達式:a.*?b
結果:aaab




2.8 分支條件

【語法】用「|」把不一樣的規則分隔開

從左到右地測試每一個條件,若是知足了某個分枝的話,就不會去再管它以後的的條件了。

\d{5}-\d{4}|\d{5}這個表達式用於匹配美國的郵政編碼。
美國郵編的規則是5位數字,或者用連字號間隔的9位數字。
之因此要給出這個例子是由於它能說明一個問題:使用分枝條件時,要注意各個條件的順序。
若是你把它改爲 \d{5}|\d{5}-\d{4}的話,那麼就只會匹配5位的郵編(以及9位郵編的前5位)。
緣由是匹配分枝條件時,將會從左到右地測試每一個條件,若是知足了某個分枝的話,就不會去再管其它的條件了。




2.9 子表達式(分組)

【語法】使用小括號「()」表示分組,分組的內容即子表達式

爲何要分組?

舉個例子

你想要在在字符串中匹配「abab"四個連續的字母,你想到使用正則表達式"ab"重複兩次,即「ab{2}」
可是其實它卻得不到你想要的結果,它匹配的是「abb」,正確的寫法是使用分組「(ab){2}」。

【例子】

匹配IP地址:IP地址由四組數字構成,每組數字由1到3個數字字符構成,它們之間以英文句號分隔。

正則表達式:(\d{1,3}.){3}\d{1,3}

且不管是否正確,可是確實是使用了分組把「\d{1.3}.」重複了三次

可是:這個模式有什麼不對的地方嗎?從語法上講,它徹底正確。說這個模式正確,是由於全部合法的IP地址都與之相匹配。但深刻研究一下就會發現,這個模式還能夠匹配其餘一些東西;說得明白點兒,不合法的IP地址也能與之相匹配。
IP地址由4個字節構成,IP地址中的4組數字分別對應着那4個字節,因此IP地址裏的每組數字的取值範圍也就是單個字節的表示範圍,即0-255。這意味着IP地址裏的每一組數字都不能大於255,但是上面那個模式還能匹配諸如34五、700、999之類的數字序列,而這些數字在P地址裏都是非法的。

注意:有句話但願你能緊緊記住:把必須匹配的狀況考慮周全並寫出一個匹配結果符合預期的正則表達式很容易,但把不須要匹配的狀況也考慮周全並確保它們都將被排除在匹配結果之外每每要困可貴多。

那麼通常怎麼使用正則表達式匹配IP地址呢?

咱們來分析一下:
下面是一個合法的IP地址裏的各組數字必須且只能符合的規則:

  • 任何一個1位或2位數字。
  • 任何一個以1開頭的3位數字。
  • 任何一個以2開頭、第2位數字在04之間的3位數字。
  • 任何一個以25開頭、第3位數字在0~5之間的3位數字

根據這些規則來構造一個相應的模式:

(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})l(1\d{2})|(2[0-4]\d)|(25[0-5]))

【注意】
1.一個「(子表達式)」就是一個分組,正則表達式的測試,默認是給他分配一個組號,並能夠顯示該組所匹配的內容。

關於組號的分配:

分組0對應整個正則表達式

實際上組號分配過程是要從左向右掃描兩遍的:第一遍只給未命名組分配,第二遍只給命名組分配--所以全部命名組的組號都大於未命名的組號

你可使用(?:exp)這樣的語法來剝奪一個分組對組號分配的參與權.(這句話生命意思呢?就時某個分組(exp),你把它寫成(?:exp),則不會單獨顯示這一組匹配的值(可是總體的正則表達式正常匹配)

2.若以咱們不須要顯示這組匹配的內容,則咱們可使用"(?:子表達式)"的形式。

eg.匹配百度雲鏈接:

(?:https?:\/\/)?(?:yun|pan|eyun)\.baidu\.com\/(?:s\/\w*(((-)?\w*)*)?|share\/\S*\d\w*)

3.咱們能夠給該分組起一個名字,使用 "(? <組名> 子表達式)"




2.10 後向引用(回溯引用)

【定義】後向引用(backreference)又稱回溯引用:經過組號或是組名對以前定義的分組所匹配到的字符串的引用

【注意】:引用的是分組匹配到的字符串,而不是分組的正則表達式。因此:你能夠把後向引用看做一個變量

後向引用方式:\組號\k <組名>

【例1】
查找文本中的重複單詞

假設你有一段文本,你想把這段文本里全部連續重複出現的單詞(打字錯誤,其中有一個單詞輸了兩遍)找出來。
顯然,在搜索某個單詞的第二次出現時,這個單詞必須是已知的。
回溯引用容許正則表達式模式引用前面的匹配結果(具體到這個例子,就是前面匹配到的單詞)。

This is a block of of text. 

正則表達式:\s+(\w+)\s+\1

結果: of of

【例2】
尋找一段標籤語言中全部合法的各級標題

這個問題並無你想的那麼簡單,你要考慮的是每一級標題的標籤的相互匹配。

文本:
<BODY>
<H1>Welcome to my Homepage</H1>
Content is divided into two sections:<BR>
<H2>C01dFusion</H2>
Information about Macromedia ColdFusion.
<H2>Wireless</H2>
Information about Bluetooth,802.11, and more.
<H2>This is not valid HTML</H3>
</BODY>

正則表達式:<[Hh]([1-6])>.*</[Hh]\1>

結果:
<H1>Welcome to my Homepage</H1>
<H2>C01dFusion</H2>
<H2>Wireless</H2>

注意:<H2>This is not valid HTML</H3>這個就不匹配

後向引用在替換中的使用:

注意在替換中,引用分組的方式是:$組號

【例子】

313-555-1234
248-555-9999
810-555-9000

正則表達式:(\d{3})(-)(\d{3})(-)(\d{4})

替換爲:($1) $3-$5

結果:
(313) 555-1234
(248) 555-9999
(810) 555-9000




2.11 先後查找(零寬斷言)

【定義】先後查找:使用正則表達式對某一個位置匹配,以後對位置的前、後內容進行查找.
【注意】匹配到的定位字符串是不返回的(專業術語:不消費)

向前查找(lookahead):在匹配的位置以前(左側)查找
【語法】:(?=exp)
【例子】

字符串:I'm singing while you're dancing.
正則表達式:\b\w+(?=ing\b)
結果:sing和danc

向後查找(lookbehind):在匹配的位置以後(右側)查找
【語法】:(?<=exp>)
【例子】

字符串:reading a book
正則表達式:(?<=\bre)\w+\b
結果:ading

把向前查找和向後查找結合起來

【例1】
正則表達式:(?<=\s)\d+(?=\s)
匹配以空白符間隔的數字(再次強調,不包括這些空白符)。

【例2】

文本:
<HEAD>
<TITLE>Ben Forta's Homepage</TITLE>
</HEAD>

正則表達式:(?<=TITLE>).+(?=</TITLE)
結果:Ben Forta's Homepage

分析:
向後查找:(?<=TITLE>),咱們匹配到「TITLE>」作爲開始定位
向前查找:(?=</TITLE), 咱們匹配到「</TITLE」做爲結束定位
(?<=TITLE>).+(?=</TITLE):這句正則表達式:即尋找一個不肯定長度的非換行符的字符串,這個字符串的開始位置是:TITLE>,結束位置是:</TITLE

【說明】
關於 「零寬斷言」 是什麼?
所謂的零寬斷言其實就是先後查找。

其實:咱們使用先後查找首先要匹配一個字符串做爲定位,這個字符串即便正則表達式最終匹配,最終匹配結果咱們也是不返回這個用來定位的字符串。即:所謂的返回的是0字節,也就是「零寬度(zero-width)」。

斷言則就是字面理解那樣--下一個結論。
正則表達式中只有當斷言爲真時纔會繼續進行匹配。

那麼這麼高大上 的名字怎麼能不使用呢!
向前查找,也稱:零寬度正預測先行斷言
向後查找,也稱:零寬度正回顧後發斷言




2.12 對先後查找取非(負向零寬斷言)

【引入】
字符集合咱們使用「^」取非,表示不去匹配這些字符,一樣向前向後查找同樣有取非。

到目前爲止正如你看到的那樣,向前查找和向後查找一般用來匹配文本,其目的是爲了肯定將被返回爲匹配結果的文本的位置(經過指定匹配結果的先後必須是哪些文本)。

這種用法被稱爲正向前查找(positive lookahead)和正向後查找(positive lookbehind)。

術語「」指的是尋找匹配的事實。

而對向前向後查找取非,則稱爲負先後查找(negative lookaround)

負先後查找的語法就是把正先後查找的等號換爲感嘆號

【定義1】負向前查找(negative lookahead)將向前查找不與給定模式相匹配的文本
【語法】:(?!exp)

【定義2】負向後查找(negative lookbehind)將向後查找不與給定模式相匹配的文本
【語法】:(?<!exp)

【例子】

文本:I paid $30 for 100 apples,50 oranges, and 60 pears.I saved $5 on this order.

只查找文本中的金額數字
分析:使用正向前查找
正則表達式:\b(?<=\$)\d+\b
結果:30和5

只查找文本中的非金額數字
分析:使用符向後查找
正則表達式:\b(?<!\$>)\d+\b
結果:100和50和60

注意:如果:(?<!\$>)\d+,會取到$30中的那個0

【分析】
\b(?<!$>)\d+\b :尋找一串數字(這串數字在文本中單獨做爲一個單詞),可是這串數字的開頭不能是"$"

【注意】深刻理解"零寬":

舉一個例子:在文本中尋找一個單詞,這個單詞中須要含有q(能夠在單詞中的任何位置),可是q後面必定不能l連着字母u

如果不使用先後查找,你可能會這樣寫:\b\wq[^u]\w\b

注意這樣寫是不許確的,如果某一個單詞以字母q結尾,也是知足咱們要尋找的單詞的要求:含有q。

可是咱們寫的那個正則表達式是匹配不到的,

爲何呢?就是由於"[^u]"是佔一個位置的,而零寬即意味着不佔位置。

因此咱們這樣寫:\b\wq(?!u)\w\b

【例子】

文本:hello benq ,I'm qik 哈哈
正則表達式:\b\w*q[^u]\w*\b
結果:qik

正則表達式:\b\w*q(?!u)\w*\b
結果:benq和qik




2.14 嵌入條件

平衡組/遞歸匹配

未完待續......




3. .NET Framework中的正則表達式

.NET Framework經過它的基本類庫提供了強大和靈活的正則表達式支持,這些支持在全部的.NET語言和工具(包括ASP.NET、C#和Visual Studio.NET在內)裏均可以使用。

NET裏的正則表達式支持是經過Regex類(以及其餘一些輔助類)提供的。

Regex類有如下一些方法:

IsMatch():測試在某個給定的字符串裏是否能夠找到一個匹配。
Match():搜索一個單個的匹配,該匹配將被爲一個Match對象。
Matches():搜索全部的匹配,它們將被返回爲一個MatchCo1lection對象。
Replace():在一個給定的字符串上進行替換操做。
Split():把一個字符串拆分爲一個字符串數組。

利用各類靜態函數,在無須建立和使用一個Regex類的狀況下也能夠執行一個正則表達式。

Regex.IsMatch():在功能上等價於IsMatch()方法。
 Regex.Match():在功能上等價於Match()方法。
 Regex.Matches():在功能上等價於Matches()方法。
 Regex.Replace():在功能上等價於Replace()方法。
 Regex.Split():在功能上等價於Split()方法。

注意事項:

  • 要想使用正則表達式,必須用Imports System.Text.Regular-Expressions語句導入正則表達式對象。

  • 若是隻是臨時須要使用正則表達式,上述靜態函數是理想的選擇。

  • 正則表達式的選項須要使用Regex.Options屬性給出,它是一個Regexoption枚舉集合,你能夠對這個枚舉集合的各有關成員如Ignorecase、Multiline、singleline等進行設置。

  • .NET支持命名捕獲,即容許對子表達式進行命名(這樣就可使用名字而不是編號來引用它們了)。
    參見:2.9 子表達式(分組)
    命名一個子表達式的語法是?
    引用這個回溯引用的語法是\k
    在一個替換模式裏引用它的語法是${name}。

  • 在使用回溯引用的時候,$ `(反引號)將返回被匹配字符串前面的全部東西,$'(單引號)將返回被匹配字符串後面的全部東西,$+將返回最後一個被匹配的子表達式,$_將返回整個原始字符串,$&將返回整個被匹配字符串。

  • .NET Framework不支持使用\E、\一、L、\u和\U進行大小寫轉換。

  • .NET Framework不支持POSIX字符類。




4. 參考

相關文章
相關標籤/搜索