正則表達式

2020-08-11 21:41:22

基本概念[ 編輯]
正則表達式(通常稱爲模式)是用於指定特定目的所需的一組字串的表達式。指定有限字串集的簡單方法是列出其元素或成員。但是,通常有更簡潔的方法來指定所需的字串集。例如,包含三個字串「Handel」,「Händel」和「Haendel」的集合可以由模式 指定H(ä|ae?)ndel; 我們說這種模式匹配三個字串中的每一個。在大多數形式主義中,如果存在至少一個與特定集匹配的正則表達式,則存在無限數量的其他正則表達式也與其匹配 - 規範不是唯一的。大多數形式主義提供了以下操作來構造正則表達式。

布爾「或」

一個豎線分隔的替代品。例如,可以匹配「灰色」或「灰色」。gray|grey

分組

括號用於定義運算子的範圍和優先順序(以及其他用途)。例如,gray|grey並且是描述「灰色」或「灰色」集合的等效模式。gr(a|e)y

定量

甲量詞一個後令牌(例如字元)或組指定前一元件被允許的頻率發生。最常見的量詞是問號 ?,星號 *(源自Kleene星)和加號 +(Kleene plus)。

? 問號表示前一個元素出現零次或一次。例如,colou?r匹配「顏色」和「顏色」。

  • 星號表示前一個元素出現零次或多次。例如,ab*c匹配「ac」,「abc」,「abbc」,「abbbc」等。
  • 加號表示前一個元素的一次或多次出現。例如,ab+c匹配「abc」,「abbc」,「abbbc」等,但不匹配「ac」。
    {n}[18] 前面的專案恰好匹配n次。
    {min,}[18] 前面的專案匹配最少或更多次。
    {min,max}[18] 前述專案匹配至少分次,但不超過最大次數。
    萬用字元

萬用字元.匹配任何字元。例如,a.b匹配任何包含「a」的字串,然後匹配任何其他字元,然後a.*b匹配「b」,匹配任何稍後包含「a」和「b」的字串。

這些結構可以組合起來形成任意複雜的表達式,就像人們可以用數位和操作+,-,×和÷構造算術表達式一樣。例如,H(ae?|ä)ndel並且都是與前面範例匹配相同字串的有效模式,。 H(a|ae|ä)ndelH(ä|ae?)ndel

正則表達式的精確語法因工具和上下文而異; 「 語法」部分提供了更多詳細資訊。

形式語言理論[ 編輯]
正則表達式在形式語言理論中描述常規語言。它們具有與常規語法相同的表達能力。

正式定義[ 編輯]
正則表達式由常數組成,表示字串集,運算子符號表示對這些集的操作。以下定義是標準的,並且在大多數關於形式語言理論的教科書中都可以找到。[19] [20]給定有限字母 Σ,以下常數被定義爲正則表達式:

(空集)∅表示集合∅。
(空字串)ε表示僅包含「空」字串的集合,該字串根本沒有字元。
Σ中的(字面字元)a表示僅包含字元a的集合。
給定正則表達式R和S,定義以下操作以生成正則表達式:

(串聯)RS表示可以通過連線R中的字串和S中的字串來獲得的字串集。例如,令R = {「ab」,「c」},並且S = {「d」,「 EF「}。然後,RS = {「abd」,「abef」,「cd」,「cef」}。
(交替)R | S表示由R和S描述的集合的集合。例如,如果R描述{「ab」,「c」}並且S描述{「ab」,「d」,「ef」},則表達式R | S描述{「ab」,「c」,「d」,「ef」}。
(Kleene星)R 表示由R描述的集閤中包含ε 的最小超集,並且在字串連線下是閉合的。這是可以通過連線R描述的集閤中的任何有限數量(包括零)字串來生成的所有字串的集合。例如,{「0」,「1」} 是所有有限二進制字串(包括空字串)的集合,{{ab「,」c「} * = {ε,」ab「,」c「 ,「abab」,「abc」,「cab」,「cc」,「ababab」,「abcab」,…}。
爲避免括號,假設Kleene星具有最高優先順序,然後連線然後交替。如果沒有歧義,則可省略括號。例如,(ab)c可以寫成abc,a|(b(c
))也可以寫成a|bc
。許多教科書使用符號∪,+或∨進行交替而不是垂直條。

例子:

a|b* 表示{ε,「a」,「b」,「bb」,「bbb」,…}
(a|b)* 表示除「a」和「b」之外沒有符號的所有字串的集合,包括空字串:{ε,「a」,「b」,「aa」,「ab」,「ba」,「bb」 ,「aaa」,…}
ab*(c|ε) 表示以「a」開頭的字串集,然後是零或更多「b」,最後可選地是「c」:{「a」,「ac」,「ab」,「abc」,「abb」,「abbc」 「,…}
(0|(1(010)1)) 表示3的倍數的二進制數集合:{ε,「0」,「00」,「11」,「000」,「011」,「110」,「0000」,「0011」,「0110」 ,「1001」,「1100」,「1111」,「00000」,…}
富有表現力和緊湊[ 編輯]
正則表達式的形式定義是故意簡約並且避免限定冗餘量詞?和+,這可以如下表示:a+= aa
,和a?= (a|ε)。有時會新增補語運算子,以提供廣義正則表達式 ; 這裏R c匹配Σ*上與R不匹配的所有字串。原則上,二補數運算子是冗餘的,因爲它總是可以通過使用其他運算子來限制。然而,用於計算這種表示的過程是複雜的,並且結果可能需要具有雙倍指數地增大的大小的表達式。[21] [22]

這種意義上的正則表達式可以表達正則語言,恰好是確定性有限自動機所接受的語言類。然而,緊湊性存在顯着差異。某些常規語言類只能通過確定性有限自動機來描述,其大小以最短等效正則表達式的大小呈指數增長。這裏的標準的例子是語言 大號ķ由所有字串過字母表{ 一,b }其ķ 日 -從-最後一個字母等於 一個。一方面,描述L 4的正則表達式由下式給出 {\ displaystyle(a \ mid b)^ {} a(a \ mid b)(a \ mid b)(a \ mid b)}(a \ mid b)^ {} a(a \ mid b)(a \ mid b)(a \ mid b)。

將此模式推廣爲L k給出表達式: {\ displaystyle(a \ mid b)^ {} a \ underbrace {(a \ mid b)(a \ mid b)\ cdots(a \ mid b)} _ {k-1 {\ text {times}} } \,}(a \ mid b)^ {} a \ underbrace {(a \ mid b)(a \ mid b)\ cdots(a \ mid b)} _ {k-1 {\ text {times}}}。 ,

另一方面,已知接受語言L k的每個確定性有限自動機必須具有至少2k個狀態。幸運的是,有一個簡單的對映,從正則表達式到更一般的非確定性有限自動機(NFA),不會導致這種大小的爆炸; 因此,NFA通常用作常規語言的替代表示。NFA是Chomsky層次結構的3型語法的簡單變體。[19]

在相反的方向上,DFA很容易描述許多語言,這些語言不容易被描述爲正則表達式。例如,確定給定ISBN號的有效性需要計算整數基數11的模數,並且可以用11狀態DFA容易地實現。然而,用11來回答相同的可除性問題的正則表達式至少是幾兆位元組的長度。[ 引證需要 ]

給定正則表達式,Thompson的構造演算法計算等價的非確定性有限自動機。通過Kleene演算法實現相反方向的轉換。

最後,值得注意的是,許多真實世界的「正則表達式」引擎實現了正式語言理論意義上的正則表達式無法描述的特徵; 相反,他們實施正則表達式。有關詳細資訊,請參見下文。

確定正則表達式的等價性[ 編輯]
如上面的許多範例所示,構造正則表達式以實現相同結果的方法不止一種。

可以編寫一種演算法,對於兩個給定的正則表達式,決定所描述的語言是否相等; 該演算法將每個表達式減少到最小確定性有限狀態機,並確定它們是否是同構的(等價的)。

正則表達式的代數定律可以使用Gischer的方法獲得,最好沿着一個例子解釋:爲了檢查(X + Y)*和(X * Y *)*是否表示相同的常規語言,對於所有正則表達式X,Y,檢查特定正則表達式(a + b)*和(a * b *)*是否表示字母表中的相同語言Σ= { a,b } 是必要且充分的}。更一般地,當且僅當具有由不同符號常數替換的不同變數的範例化成立時,具有變數的正則表達式項之間的等式E = F成立。[23] [24]

通過使用Kleene star和set union來找到仍然完全表達的正則表達式的有趣子集,可以消除冗餘,但也許可以限制它們的使用。[ 需要澄清 ]這是一個令人驚訝的難題。就像正則表達式一樣簡單,沒有方法可以系統地將它們重寫爲某種正常形式。過去缺乏公理導致了星高問題。1991年,Dexter Kozen使用等式和Horn子句公理將正則表達式公理化爲Kleene代數。[25] 早在1964年,雷德科已經證明,沒有一套有限的純粹等式公理可以表徵常規語言的代數。[26]

語法[ 編輯]
正則表達式模式匹配目標字串。該模式由一系列原子組成。原子是正則表達式模式中的單個點,它嘗試與目標字串匹配。最簡單的原子是文字,但匹配原子的模式的分組部分將需要使用( )元字元。元字元有助於形成:原子 ; 量詞告訴了多少原子(以及它是否是一個貪婪的量詞或不); 一個邏輯OR字元,它提供了一組備選方案,以及一個邏輯NOT字元,它否定了原子的存在; 和反向參照指的是完成原子模式的先前原子。進行匹配,而不是當字串的所有原子匹配時,而是當正則表達式中的所有模式原子匹配時。我們的想法是讓一個小的字元模式代表大量可能的字串,而不是編譯所有字面可能性的大型列表。

根據正則表達式處理器,大約有14個元字元,字元可能有也可能沒有字面字元含義,具體取決於上下文,或者它們是否「被跳脫」,即前面是跳脫序列,在這種情況下是反斜槓\。現代和POSIX擴充套件正則表達式使用元字元比它們的字面含義更常用,因此爲了避免「反斜槓」或傾斜牙籤綜合症,將元字元轉換爲字面模式是有意義的; 但是從最初開始,讓四個包圍元字元( )並且{ }主要是字面意義更有意義,並且「逃避」這個通常意義成爲元字元。通用標準同時實現。 {}^$.|*+?\。跳脫時成爲元字元的常用字元是dswDSW和N。

分隔符[ 編輯]
當用程式語言輸入正則表達式時,它們可以表示爲通常的字串文字,因此通常被參照; 這在C,Java和Python中很常見,例如re輸入正則表達式"re"。但是,它們通常用斜槓作爲分隔符編寫,就像/re/正則表達式一樣re。這源於ed,其中/是用於搜尋的編輯器命令,並且表達式/re/可用於指定一系列行(匹配模式),這些行可以與任何一方的其他命令組合,最着名的g/re/p是grep(「global」正則表達式列印「),它包含在大多數基於Unix的操作系統中,例如Linux分佈。在sed中使用了類似的約定,其中搜尋和替換由給定,s/re/replacement/並且模式可以用逗號連線以指定一系列行,如/re1/,/re2/。由於它在Perl中的使用,這種表示法特別衆所周知,它在語法中構成了與普通字串文字不同的部分語法。在某些情況下,例如sed和Perl,可以使用替代分隔符來避免與內容衝突,並避免必須跳脫內容中出現的分隔符字元。例如,在sed命令s,/,X,將取代/具有X,使用逗號作爲分隔符。

標準[ 編輯]
在IEEE POSIX標準有三套合規性:BRE(基本正則表達式),[27] ERE(擴充套件正則表達式),和SRE(簡單的正則表達式)。SRE被棄用,[28]贊成BRE,因爲它們都提供了向後相容性。以下有關字元類的小節適用於BRE和ERE。

BRE和ERE一起工作。ERE增加?,+以及|和它不再需要爲了躲避元字元( )和{ },這是需要在BRE。此外,只要遵守正則表達式的POSIX標準語法,就可以並且通常是附加語法來爲特定(但POSIX相容)應用程式提供服務。雖然POSIX.2保留了一些未定義的實現細節,但BRE和ERE提供了一個「標準」,後來被用作許多工具的預設語法,其中BRE或ERE模式的選擇通常是支援的選項。例如,GNU grep有以下選項:ERE爲「grep -E」,BRE爲「grep -G」(預設值),Perl正則表達式爲「grep -P」。

Perl正則表達式已成爲事實上的標準,具有豐富而強大的原子表達式集。Perl沒有「基本」或「擴充套件」級別。如在POSIX ERES,( )並且{ }被視爲元字元除非跳脫; 已知其他元字元僅基於上下文是字面的或符號的。其他功能包括延遲匹配,回溯,命名捕獲組和遞回模式。

POSIX基本和擴充套件[ 編輯]

在POSIX標準,基本正語法(BRE)要求的元字元 ( )和{ }指定()和{},而擴充套件正語法(ERE)沒有。

元字元 描述
^ 匹配字串中的起始位置。在基於行的工具中,它匹配任何行的起始位置。
. 匹配任何單個字元(許多應用程式排除換行符,並且確切地說哪些字元被視爲換行符是flavor,character-encoding-和特定於平臺,但可以安全地假設包含換行符)。在POSIX括號表達式中,點字元與文字點匹配。例如,a.c匹配「abc」等,但[a.c]僅匹配「a」,「。」或「c」。
[ ] 括號表達式。匹配括號內包含的單個字元。例如,[abc]匹配「a」,「b」或「c」。[a-z]指定一個範圍,它匹配從「a」到「z」的任何小寫字母。這些形式可以混合:[abcx-z]匹配「a」,「b」,「c」,「x」,「y」或「z」,如同[a-cx-z]。
如果-字元是括號內的最後一個或第一個(如果存在的話)字元,則該字元被視爲文字字元:[abc-],[-abc]。請注意,不允許使用反斜槓跳脫。如果]字元是第一個(在)字元之後,則該字元可以包含在括號表達式中:[]abc]。

[^ ] 匹配括號內未包含的單個字元。例如,[abc]匹配「a」,「b」或「c」以外的任何字元。[a-z]匹配從「a」到「z」不是小寫字母的任何單個字元。同樣,文字字元和範圍可以混合使用。
$ 匹配字串的結束位置或字串結尾換行符之前的位置。在基於行的工具中,它匹配任何行的結束位置。
( ) 定義標記的子表達式。可以在以後呼叫括號內匹配的字串(請參閱下一個條目)。標記的子表達式也稱爲塊或捕獲組。BRE模式需要。 \n( )
\n 匹配第n個標記的子表達式匹配的內容,其中n是1到9的數位。這個結構在POSIX.2標準中含糊不清。某些工具允許參照超過9個捕獲組。

  • 匹配前面的元素零次或多次。例如,ab*c匹配「ac」,「abc」,「abbbc」等[xyz]*匹配「」,「x」,「y」,「z」,「zx」,「zyx」,「xyzzy」等。(ab)*匹配「」,「ab」,「abab」,「ababab」等。
    {m,n} 匹配前面的元素至少m次並且不超過n次。例如,a{3,5}僅匹配「aaa」,「aaaa」和「aaaaa」。在一些舊版本的正則表達式中找不到這個。BRE模式需要{m,n}。
    例子:

.at 匹配以「at」結尾的任何三個字元的字串,包括「hat」,「cat」和「bat」。
[hc]at 匹配「帽子」和「貓」。
[^b]at匹配.at除「bat」以外的所有匹配的字串。
[^hc]at匹配.at除「hat」和「cat」以外的所有匹配的字串。
1at 匹配「hat」和「cat」,但僅限於字串或行的開頭。
[hc]at$ 匹配「hat」和「cat」,但僅限於字串或行的末尾。
[.] 匹配由「[」和「]」包圍的任何單個字元,因爲括號被跳脫,例如:「[a]」和「[b]」。
s.* 匹配s後跟零個或多個字元,例如:「s」和「saw」和「seed」。
POSIX擴充套件[ 編輯]

對於POSIX擴充套件正則表達式(ERE)語法中的某些字元,使用反斜槓跳脫的元字元的含義相反。使用此語法,反斜槓會將元字元視爲文字字元。所以,例如,現在和現在。此外,刪除了對反向參照的支援,並新增了以下元字元: ( )( ){ }{ }\n

元字元 描述
? 匹配前面的元素零次或一次。例如,ab?c僅匹配「ac」或「abc」。

  • 匹配前面的元素一次或多次。例如,ab+c匹配「abc」,「abbc」,「abbbc」等,但不匹配「ac」。
    | 選擇(也稱爲交替或集合併)運算子匹配運算子之前的表達式或運算子之後的表達式。例如,abc|def匹配「abc」或「def」。
    例子:

[hc]?at 匹配「at」,「hat」和「cat」。
[hc]*at 匹配「at」,「hat」,「cat」,「hhat」,「chat」,「hcat」,「cchchat」等。
[hc]+at 匹配「hat」,「cat」,「hhat」,「chat」,「hcat」,「cchchat」等,但不是「at」。
cat|dog 匹配「貓」或「狗」。
通過包含命令列標誌-E, POSIX擴充套件正則表達式通常可以與現代Unix實用程式一起使用。

字元類[ 編輯]

字元類是文字匹配後最基本的正則表達式概念。它使一個小的字元序列匹配更大的字元集。例如,可以代表大寫字母,可以表示任何數位。字元類適用於兩種POSIX級別。 [A-Z]\d

指定字元範圍(例如小寫到大寫)時,計算機的區域設定通過字元編碼的數位排序來確定內容。它們可以儲存該序列中的數位,或者排序可以是abc … zABC … Z或aAbBcC … zZ。所以POSIX標準定義了一個字元類,它將由安裝的正則表達式處理器知道。這些定義如下表所示: [a-Z]az

POSIX 非標 的Perl / Tcl的 VIM Java的 ASCII 描述
[:ascii:][29] \p{ASCII} [\x00-\x7F] ASCII字元
[:alnum:] \p{Alnum} [A-Za-z0-9] 字母數位字元
[:word:][29] \w \w \w [A-Za-z0-9_] 字母數位字元加「
\W \W \W [^A-Za-z0-9
] 非單詞字元
[:alpha:] \a \p{Alpha} [A-Za-z] 字母字元
[:blank:] \s \p{Blank} [ [[\t]]] 空格和標籤
\b < > \b (?<=\W)(?=\w)|(?<=\w)(?=\W) 字界限
\B (?<=\W)(?=\W)|(?<=\w)(?=\w) 非字邊界
[:cntrl:] \p{Cntrl} [\x00-\x1F\x7F] 控制字元
[:digit:] \d \d \p{Digit} 要麼 \d [0-9] 數位
\D \D \D [^0-9] 非數位
[:graph:] \p{Graph} [\x21-\x7E] 可見字元
[:lower:] \l \p{Lower} [a-z] 小寫字母
[:print:] \p \p{Print} [\x20-\x7E] 可見字元和空格字元
[:punct:] \p{Punct} [][!"#$%&’()*+,./:;<=>?@^_`{|}~-] 標點字元
[:space:] \s _s \p{Space} 要麼 \s [ \t\r\n\v\f] 空白字元
\S \S \S [^ \t\r\n\v\f] 非空白字元
[:upper:] \u \p{Upper} [A-Z] 大寫字母
[:xdigit:] \x \p{XDigit} [A-Fa-f0-9] 十六進制數位
POSIX字元類只能在括號表達式中使用。例如,匹配大寫字母和小寫「a」和「b」。 [[:upper:]ab]

某些工具可以理解的另一個非POSIX類,通常定義爲加下劃線。這反映了這樣一個事實,即在許多程式語言中,這些是可以在識別符號中使用的字元。編輯器Vim進一步區分單詞和詞頭類(使用符號和),因爲在許多程式語言中,可以開始識別符號的字元與可以在其他位置出現的字元不同。 [:word:][:alnum:]\w\h

請注意,POSIX正則表達式標準呼叫字元類通常被稱爲支援它們的其他正則表達式中的POSIX字元類。對於大多數其他正則表達式,術語字元類用於描述POSIX呼叫括號表達式。

Perl和PCRE [ 編輯]
另請參閱:Perl相容的正則表達式

由於其表達能力和(相對)易讀性,許多其他實用程式和程式語言採用了類似於Perl的語法 - 例如,Java,JavaScript,Python,Ruby,Qt,Microsoft的.NET Framework和XML Schema。一些語言和工具,如Boost和PHP支援多種正則表達式風格。Perl派生的正則表達式實現並不相同,通常實現1994年發佈的Perl 5.0中的功能子集.Perl有時會包含最初在其他語言中發現的功能,例如,Perl 5.10實現了最初在PCRE和Python中開發的語法擴充套件。[30]

懶人匹配[ 編輯]
在Python和其他一些實現(例如Java)中,三個常見的量詞(*,+和?)預設是貪婪的,因爲它們匹配儘可能多的字元。[31]".+"應用於字串的正則表達式(包括引號)

「Ganymede,」他繼續道,「是太陽系中最大的衛星。」
匹配整行而不是僅匹配第一個單詞"Ganymede,"。上述量詞可能,但是,作出懶惰或極少或不情願,匹配儘可能少的字元可能通過附加一個問號:".+?「只匹配"Ganymede,」。[31]

但是,這並不能確保在某些情況下整個句子不匹配。問號運算子不會更改點運算子的含義,因此這仍然可以匹配輸入中的引號。".*?" EOF如果這是字串,那麼類似的模式仍將匹配整個輸入

「Ganymede,」他繼續道,「是太陽系中最大的衛星。」 EOF
爲了確保引號不能成爲匹配的一部分,必須替換點,例如:"[^"]*"這將匹配參照的文字部分,而不包含其他引號。

佔有匹配[ 編輯]
在Java中,量詞可以通過附加加號來佔有慾,這會禁用後退,即使這樣做會使整個匹配成功:[32]正則表達式".*"應用於字串

「Ganymede,」他繼續道,「是太陽系中最大的衛星。」
匹配整行,正則表達式".+"根本不匹配,因爲.+消耗了整個輸入,包括最終輸入"。因此,佔有量詞對於否定的字元類是最有用的,例如"[^"]*+","Ganymede,"當應用於相同的字串時匹配。

佔有量詞比貪婪和惰性量詞更容易實現,並且通常在執行時更有效。[32]

非常規語言的模式[ 編輯]
幾乎所有現代正則表達式庫中的許多功能都提供了遠遠超過常規語言的表達能力。例如,許多實現允許使用括號對子表達式進行分組,並在同一表達式中回憶它們匹配的值(反向參照)。這意味着,除其他外,模式可以匹配重複單詞的字串,如「papa」或「WikiWiki」,在形式語言理論中稱爲正方形。這些字串的模式是(.+)\1。

由於泵浦引理,正方形的語言不規則,也沒有上下文。然而,由衆多現代工具支援的模式匹配與無限數量的反向參照仍然是上下文敏感的。[33]

但是,許多提供此類構造的工具,庫和引擎仍然使用術語正則表達式作爲其模式。這導致了術語正則表達在形式語言理論和模式匹配中具有不同含義的術語。出於這個原因,一些人已經採取措施來使用期限的正則表達式,正則表達式,或者乾脆模式來描述後者。Perl程式語言的作者Larry Wall在一篇關於Perl 6設計的文章中寫道:

「正則表達式」[…]僅與實際正則表達式略有關係。儘管如此,這個術語隨着模式匹配引擎的功能而增長,所以我不打算在這裏嘗試對抗語言必需性。然而,我會將它們稱爲「正則表達式」(或「regexen」,當我處於盎格魯 - 撒克遜的情緒時)。[17]

實施和執行時間[ 編輯]
至少有三種不同的演算法決定給定的正則表達式是否以及如何與字串匹配。

最古老和最快的依賴於形式語言理論的結果,該理論允許將每個非確定性有限自動機(NFA)轉換爲確定性有限自動機(DFA)。可以顯式構造DFA,然後一次一個符號在結果輸入字串上執行。爲大小爲m的正則表達式構造DFA 的時間和記憶體成本爲O(2 m),但它可以在時間O(n)的大小爲n的字串上執行。

另一種方法是直接模擬NFA,基本上按需構建每個DFA狀態,然後在下一步丟棄它。這樣可以隱藏DFA並避免指數構建成本,但執行成本會上升到O(mn)。顯式方法稱爲DFA演算法,隱式方法稱爲NFA演算法。將快取新增到NFA演算法通常稱爲「延遲DFA」演算法,或者僅稱爲DFA演算法而不進行區分。這些演算法很快,但使用它們來呼叫分組子表達式,延遲量化和類似特徵是棘手的。[34] [35]

第三種演算法是通過回溯將模式與輸入字串進行匹配。此演算法通常稱爲NFA,但此術語可能會令人困惑。它的執行時間可以是指數級的,當與包含交替和無界量化的表達式匹配時,簡單的實現表現出來,並迫使演算法考慮指數增加的子案例數。此行爲可能會導致稱爲正則表達式拒絕服務的安全問題。 (a|aa)*b

雖然回溯實現僅在最壞的情況下給出指數保證,但它們提供了更大的靈活性和表現力。例如,任何允許使用反向參照或實現Perl引入的各種擴充套件的實現都必須包含某種回溯。一些實現[ 哪些?]嘗試通過首先執行快速DFA演算法來提供最好的兩個演算法的,並恢復到僅當匹配過程中遇到一個反向參照潛在較慢回溯演算法。

Unicode [ 編輯]
在理論上,任何令牌集都可以由正則表達式匹配,只要它是預定義的。在歷史實現方面,儘管正則表達式庫支援許多其他字元集,但最初編寫的正則表達式使用ASCII字元作爲其令牌集。許多現代正則表達式引擎至少提供一些Unicode支援。在大多數方面,字元集的含義沒有區別,但在擴充套件正則表達式以支援Unicode時會出現一些問題。

支援的編碼。一些正則表達式庫期望在某些特定編碼上工作,而不是在抽象Unicode字元上工作。其中許多需要UTF-8編碼,而其他人可能需要UTF-16或UTF-32。相比之下,Perl和Java在編碼上是不可知的,而是在內部對解碼的字元進行操作。
支援的Unicode範圍。許多正則表達式引擎僅支援基本多語言平面,即只能用16位元編碼的字元。目前(截至2016年)只有少數正則表達式引擎(例如,Perl和Java)可以處理完整的21位Unicode範圍。
將面向ASCII的構造擴充套件爲Unicode。例如,在基於ASCII的實現方式中,表格的字元範圍[x-y]是有效的任何地方X和ÿ有程式碼點在範圍[0x00,0x7F]和碼點(X)≤碼點(Ý)。將此類字元範圍自然擴充套件爲Unicode只會將端點位於[0x00,0x7F]的要求更改爲它們位於[0x0000,0x10FFFF]的要求。但是,實際上通常情況並非如此。一些實現,例如gawk的實現,不允許字元範圍跨越Unicode塊。類似[0x61,0x7F]的範圍是有效的,因爲兩個端點都在Basic Latin塊內,因爲[0x0530,0x0560]因爲兩個端點都在亞美尼亞塊內,但是像[0x0061,0x0532]這樣的範圍是無效的,因爲它包括多個Unicode塊。其他引擎(例如Vim編輯器的引擎)允許塊交叉,但字元值不得超過256。[36]
不區分大小寫。一些不區分大小寫的標誌僅影響ASCII字元。其他標誌會影響所有字元。有些引擎有兩個不同的標誌,一個用於ASCII,另一個用於Unicode。究竟屬於POSIX類的字元也各不相同。
堂兄弟的案件不敏感。由於ASCII具有區分大小寫,因此不區分大小寫成爲文字搜尋中的邏輯特徵。Unicode引入了字母指令碼,沒有像Devanagari這樣的案例。對於這些,區分大小寫不適用。對於像中文這樣的指令碼,另一個區別似乎是合乎邏輯的:在傳統和簡 在阿拉伯文字中,可能需要對初始,內側,最終和孤立位置不敏感。在日語中,平假名和片假名之間的不敏感有時是有用的。
規範化。Unicode具有組合字元。與舊打字機一樣,普通字母後面可以跟一個或多個非間距符號(通常是重音符號,如重音符號)以形成單個列印字元,但也提供預組合字元,即已包含一個或多個組合字元的字元。字元+組合字元的序列應與相同的單個預組合字元匹配。標準化字元序列+組合字元的過程稱爲標準化。
新的控制程式碼。Unicode引入了其他,位元組順序標記和文字方向標記。這些程式碼可能必須以特殊方式處理。
引入Unicode塊,指令碼和許多其他字元屬性的字元類。塊屬性遠不如指令碼屬性有用,因爲塊可以包含來自多個不同指令碼的程式碼點,而指令碼可以具有來自多個不同塊的程式碼點。[37]在Perl和java.util.regex庫中,塊X和/ 中的表單\p{InX}或\p{Block=X}匹配字元的屬性匹配不在該塊中的程式碼點。同樣,,,或在亞美尼亞文字的任何字元匹配。通常,將任何字元與二進制屬性X或常規類別X匹配\P{InX}\P{Block=X}\p{Armenian}\p{IsArmenian}\p{Script=Armenian}\p{X}。例如\p{Lu},\p{Uppercase_Letter}或者\p{GC=Lu}匹配任何大寫字母。二進制性質是不一般的類別包括\p{White_Space},\p{Alphabetic},\p{Math},和\p{Dash}。非二進制性質的範例是\p{Bidi_Class=Right_to_Left},\p{Word_Break=A_Letter},和\p{Numeric_Value=10}。
使用[ 編輯]
正則表達式在各種文字處理任務中很有用,更常見的是字串處理,其中數據不必是文字的。常見的應用程式包括數據驗證,數據抓取(尤其是網頁抓取),數據爭論,簡單解析,語法高亮系統的製作以及許多其他任務。

雖然正則表達式在Internet 搜尋引擎上很有用,但是根據正則表達式的複雜性和設計,在整個數據庫中處理它們可能會消耗過多的計算機資源。雖然在許多情況下系統管理員可以在內部執行基於正則表達式的查詢,但大多數搜尋引擎都不向公衆提供正則表達式支援。值得注意的例外:Google程式碼搜尋,Exalead。截至2012年1月,Google程式碼搜尋已關閉。[38] 它使用了一個trigram索引來加速查詢。[39]

範例[ 編輯]
具體的語法規則取決於具體的實現,程式語言或正在使用的庫。此外,正則表達式實現的功能可能因版本而異。

因爲正則表達式很難在沒有範例的情況下解釋和理解,所以用於測試正則表達式的互動式網站是通過實驗學習正則表達式的有用資源。本節通過說明的方式提供了正則表達式的一些屬性的基本描述。

範例中使用以下約定。[40]

元字元;; metacharacters列指定正在演示的正則表達式語法
= ~m // ;; 表示Perl中的正則表達式匹配操作
= ~s /// ;; 表示Perl中的正則表達式替換操作
另外值得注意的是,這些正則表達式都是類似Perl的語法。標準POSIX正則表達式是不同的。

除非另有說明,否則以下範例符合Perl程式語言,版本5.8.8,2006年1月31日。這意味着其他實現可能缺乏對此處所示語法的某些部分的支援(例如,基本與擴充套件正則表達式,( )對比(),或缺乏\d而不是POSIX[:digit:])。

這些範例中使用的語法和約定也與其他程式設計環境的語法和約定一致。[41]


字元 描述 例子[42]
. 通常匹配除換行符之外的任何字元。
在方括號內,點是字面的。
$ string1 = 「Hello World \ n」 ;
如果 (KaTeX parse error: Expected '}', got 'EOF' at end of input: …../ ) { 列印 「字串1具有長度> = 5。\ n」個;
}
輸出:

你好,世界
長度> = 5。
( ) 將一系列模式元素分組到單個元素。
當你匹配括號內的模式,你可以使用任何的$1,$2…後來指先前匹配的模式。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m /(H …)。(o …)/ ) {
print 「我們匹配’$ 1’和’$ 2’。\ n」 ;
}
輸出:

我們匹配’Hel’和’o W’。

  • 匹配前面的模式元素一次或多次。
    $ string1 = 「Hello World \ n」 ;
    if ($ string1 = ~ m / l + / ) {
    print 「 $ string1中有一個或多個連續的字母\」l \「。\ n」 ;
    }
    輸出:

Hello World中有一個或多個連續的字母「l」。
? 匹配前面的模式元素零次或一次。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / H。?e / ) {
print 「有’H’和’e’分隔」 ;
列印 「0-1個字元(例如,He Hue Hee)。\ n」 ;
}
輸出:

有一個’H’和’e’以0-1個字元分隔(例如,He Hue Hee)。
? 修改*,+,?或者{M,N}「d自帶之前的幾次地匹配正則表達式。
$ string1 = 「Hello World \ n」 ;
如果 ($字串1 =〜 米/(1 + O)/? ) {
列印 「的非貪婪匹配’L’後面跟着一個或\ n」個;
列印 「更多的字元是’llo’而不是’llo Wo’。\ n」 ;
}
輸出:

與’l’的非貪婪匹配後跟一個或
更多的人物是’llo’而不是’llo Wo’。

  • 匹配前面的模式元素零次或多次。
    $ string1 = 「Hello World \ n」 ;
    if ($ string1 = ~ m / el * o / ) {
    print 「有一個’e’後跟零到多個」 ;
    列印 「‘l’後跟’o’(例如,eo,elo,ello,elllo)。\ n」 ;
    }
    輸出:

有一個’e’後跟零到多’l’後跟’o’(例如,eo,elo,ello,elllo)。
{M,N} 表示最小M和最大N匹配計數。
N可以省略,M可以是0:{M}匹配「完全」M次; {M,}匹配「至少」M次; {0,N}匹配「最多」N次。
x* y+ z?因此相當於x{0,} y{1,} z{0,1}。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / l {1,2} / ) {
print 「存在一個至少爲1的子字串」 ;
列印 「和$ string1 \ n中最多2 l」 ;
}
輸出:

在Hello World中存在至少1和最多2 l的子串
[…] 表示一組可能的角色匹配。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / [aeiou] + / ) {
print 「$ string1包含一個或多個元音。\ n」 ;
}
輸出:

你好,世界
包含一個或多個元音。
| 分開其他可能性。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m /(Hello | Hi | Pogo)/ ) {
print 「$ string1至少包含Hello,Hi或Pogo中的一個。」 ;
}
輸出:

你好,世界
包含Hello,Hi或Pogo中的至少一個。
\b 匹配單詞類字元(參見下一個)和非單詞類字元或邊緣之間的零寬度邊界; 與…一樣
(^\w|\w$|\W\w|\w\W)。

$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / llo \ b / ) {
print 「有一個單詞以’llo’結尾。\ n」 ;
}
輸出:

有一個單詞以’llo’結尾。
\w 匹配字母數位字元,包括「」;
與[A-Za-z0-9
]ASCII 相同,和
[\p{Alphabetic}\p{GC=Mark}\p{GC=Decimal_Number}\p{GC=Connector_Punctuation}]

在Unicode中,[37]其中Alphabetic屬性包含多個拉丁字母,並且該Decimal_Number屬性包含多個阿拉伯數位。

$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / \ w / ) {
print 「至少有一個字母數位」 ;
列印 $ string1中的字元(AZ,az,0-9,_)。\ n「 ;
}
輸出:

Hello World中至少有一個字母數位字元
(AZ,az,0-9,)。
\W 匹配非字母數位字元,不包括「
」;
與[^A-Za-z0-9_]ASCII 相同,和
[^\p{Alphabetic}\p{GC=Mark}\p{GC=Decimal_Number}\p{GC=Connector_Punctuation}]

在Unicode中。

$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / \ W / ) {
print 「Hello和」之間的空格;
列印 「世界不是字母數位。\ n」 ;
}
輸出:

Hello和World之間的空格不是字母數位。
\s 匹配空格字元,
ASCII格式爲製表符,換行符,換頁符,回車符和空格;
在Unicode中,還匹配無中斷空格,下一行和可變寬度空間(以及其他)。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / \ s。* \ s / ) {
print 「在$ string1中有兩個空白字元,可能是」 ;
列印 「由其他字元分隔。\ n」 ;
}
輸出:

在Hello World中
有兩個空格字元,可以用其他字元分隔。
\S 匹配任何東西但是空白。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / \ S。* \ S / ) {
print 「在$ string1中有兩個非空白字元,」 ;
print 「可能被其他字元分隔。\ n」 ;
}
輸出:

在Hello World中
有兩個非空白字元,可以用其他字元分隔。
\d 匹配一個數字;
與[0-9]ASCII 相同;
在Unicode中,與\p{Digit}or \p{GC=Decimal_Number}屬性相同,它本身與\p{Numeric_Type=Decimal}屬性相同。
$ string1 = 「牆上掛着99瓶啤酒。」 ;
if ($ string1 = ~ m /(\ d +)/ ) {
print 「$ 1是’$ string1’中的第一個數位\ n」 ;
}
輸出:

99年是「99瓶啤酒在牆上的第一個數位」。
\D 匹配非數位;
與[^0-9]ASCII或\P{Digit}Unicode相同。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / \ D / ) {
print 「$ string1中至少有一個字元」 ;
列印 「這不是數位。\ n」 ;
}
輸出:

Hello World中至少有一個字元
這不是一個數字。
^ 匹配行或字串的開頭。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / ^ He / ) {
print 「$ string1以字元’He’開頭。\ n」 ;
}
輸出:

你好,世界
從字元’他’開始。
$ 匹配行或字串的結尾。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / rld $ / ) {
print 「$ string1是一個行或字串」 ;
列印 「以’rld’結尾。\ n」 ;
}
輸出:

你好,世界
是以’rld’結尾的行或字串。
\A 匹配字串的開頭(但不是內部行)。
$ string1 = 「Hello \ nWorld \ n」 ;
if ($ string1 = ~ m / \ AH / ) {
print 「$ string1是一個字串」 ;
列印 「以’H’開頭。\ n」 ;
}
輸出:

你好
世界
是一個以’H’開頭的字串。
\z 匹配字串的結尾(但不是內部行)。[43]
$ string1 = 「Hello \ nWorld \ n」 ;
if ($ string1 = ~ m / d \ n \ z / ) {
print 「$ string1是一個字串」 ;
列印 「以’d \ n’結尾。\ n」 ;
}
輸出:

你好
世界
是一個以’d \ n’結尾的字串。
[^…] 匹配除括號內的每個字元。
$ string1 = 「Hello World \ n」 ;
if ($ string1 = ~ m / [^ abc] / ) {
print 「$ string1包含除」以外的字元「 ;
列印 「a,b和c。\ n」 ;
}
輸出:

你好,世界
包含a,b和c以外的字元。


  1. hc ↩︎