pcre和正則表達式的誤點

本文只是關於正則一些容易出錯的地方,關於正則的學習,可參考以下兩篇文章:html

基礎正則:http://www.javashuo.com/article/p-matsrdxc-bw.htmlpython

Perl正則:http://www.javashuo.com/article/p-tskgyeoq-ky.htmlgit

1.正則中全部的匹配模式,都應該理解爲"匹配了某字符或字符串後,緊跟着再匹配"。這個概念很重要。正則表達式

2.中括號首部使用脫字符時,表示的是緊跟着匹配不含給定字符的字符,而不是容許不匹配給定的字符。
它們大多數時候是等價的,但在匹配行尾時,意義不一樣,例如:Aa[^bcd]$ 所匹配的行容許是Aaa$或Aax$,但不容許僅是Aa$。
這就是正則中"緊跟着匹配"的意思。c#

3.(\.[0-9]+)? 可匹配小數點部分,不能寫成 (\.?[0-9]*) ,後者即便不能匹配小數點,也能匹配本來處於小數點後的數值ruby

4.星號*匹配問題0或多個字符,若是寫成"a*",將能夠匹配任何單個字符,只不過對於非a字符,匹配到的結果爲空。ide

例如字符串"111aaaAAA",正則"1a*"其實從讀取第2個字符進行匹配的時候就已經匹配完成了,匹配的結果不是"1aaa",而是"1"加上1前面的一個空,由於是從行首這個錨定位開始匹配的。使用sed或perl進行替換操做就很容易理解。學習

$ echo '111aaaAAA' | perl -lne 'print s/a*/b/rg'
b1b1b1bbAbAbAb        # 每個非a字符前都被替換了

$ echo '111aaaAAA' | perl -lne "/1a*/;print $&;print $'"
1                   # 表示匹配到的內容
11aaaAAA       # 表示匹配後剩下的內容

這可能不是很好理解。但其實想一想".*"的做用,其實他們是同一類的寫法,正如".*"並非先用點去匹配一個字符後再經過星號去重複這個字符,而是直接表示匹配可能任意多個的任一字符。不一樣之處在於,".*"匹配任何單個字符,而"a*"匹配任何單個字符,包括空位置。測試

另外,有些語言在處理正則的方式上有些差別,特別是在測試"a*"的時候,grep/sed/perl均有所不一樣,不過python/perl/ruby之類的語言在處理這個問題上,結果都同樣。this

5.perl正則括號分組時,使用(?:替代左括號(,能夠表示只分組不捕獲。所謂的捕獲表示的是能夠反向引用或保存到正則外部的變量中
([-+]?[0-9]+(\.[0-9]+)?) *(cm|mm) :(cm|mm)將保存爲$3
([-+]?[0-9]+(?:\.[0-9]+)?) *(cm|mm) : (cm|mm)將保存爲$2

6.特殊錨定符,錨定所匹配的是位置,而非字符,行首^和行尾$一樣如此。
注意某些程序對單詞的理解和邊界定義不同。且有些程序並不徹底支持下列全部的特殊元字符。通常來講,單詞是由字母、數字和下劃線組成的,即[a-zA-Z0-9_]。
例如gnu grep 2.6版本就不支持\s和\d,而gnu grep 2.20支持\s但不支持\d
'\b':匹配單詞邊界處的空字符Match the empty string at the edge of a word.
'\B':匹配非單詞邊界處的空字符Match the empty string provided it's not at the edge of a word.
'\<':匹配單詞開頭處的空字符Match the empty string at the beginning of word.
'\>':匹配單詞結尾處的空字符Match the empty string at the end of word.
'\w':匹配單詞構成部分Match word constituent, it is a synonym for `[_[:alnum:]]'.
'\W':匹配非單詞構成部分Match non-word constituent, it is a synonym for `[^_[:alnum:]]'.
'\s':匹配空白字符Match whitespace, it is a synonym for `[[:space:]]'.
'\S':匹配非空白字符Match non-whitespace, it is a synonym for `[^[:space:]]'.
'\d':匹配數字it is a synonym for `[0-9]'.
'\D':匹配非數字it is a synonym for `[^0-9]'.

For example, '\brat\b' matches the separate word 'rat', '\Brat\B' matches 'crate' but not 'furry rat'.

7.字符類,注意某些程序並不徹底支持下列全部的字符類
'[:alnum:]' :same as '[0-9A-Za-z]'.
'[:alpha:]' :'[:lower:]' and '[:upper:]', same as '[A-Za-z]'.
'[:lower:]' :
'[:upper:]' :
'[:digit:]' :'0 1 2 3 4 5 6 7 8 9'.
'[:xdigit:]' :Hex digits: `0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f'.

'[:blank:]' :space and tab.
'[:space:]' :tab, newline, vertical tab, form feed, carriage return, and space.
'[:punct:]' :Punctuation characters; this is '! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~'.
'[:print:]' :'[:alnum:]', '[:punct:]', and space.
'[:graph:]' :Graphical characters: '[:alnum:]' and '[:punct:]'.

'[:cntrl:]' :Control characters. octal codes 000 through 037, and 177 (`DEL').

8.同一個表達式中,被匹配過的字符沒法被第二次匹配。由於正則的宗旨是:匹配了某字符或字符串後,緊跟着再匹配。
例如字符串"#c#",正則表達式"(#.)(.#)"沒法匹配。
再例如字符串"#cc#",正則表達式"(#.)(.*)(.#)"能匹配成功,只不過第二個分組只能匹配空。

9."環視"錨定,即lookaround anchor(也稱爲"零寬斷言",表示匹配的是位置,不是字符)。
以 (?= 替代左括號表示從左向右的順序環視,例如(?=\d)表示當前字符的右邊是一個數字時就知足條件
以 (?<= 替代左括號表示從右向左的逆序環視,例如(?<=\d)表示當前字符的左邊是一個數字時就知足條件

  • 正向環視:(?=...)和(?!...),感嘆號表否認,即沒法匹配感嘆號右邊的字符時才捕獲。
  • 逆向環視:(?<=...)和(?<!...)

逆向環視的表達式必須只能表示固定長度的字符串,例如(?<=word)或(?<=word|word)能夠,但(?<=word?)不能夠,由於?匹配0或1長度,長度不定。
在PCRE中,可重寫爲(?<=word|words),但perl中不容許,由於perl嚴格要求長度必須固定。


10.關於"環視"錨定,最須要注意的一點是匹配的結果不佔用任何字符,它僅僅只是錨定位置。
例如:your name is longshuai MA 和 your name is longfei MA
使用(?=longshuai)將能錨定第一個句子中單詞"longshuai"前面的空字符,但它的匹配結果是"longshuai"前的空白字符,
因此(?=longshuai)long才能表明"long"這幾個字符串
因此僅對於此處的兩個句子,long(?=shuai)和(?=longshuai)long是等價的

11.貪婪匹配、惰性匹配和佔有優先匹配
默認狀況下,對於重複次數的表達式都是貪婪匹配,表示儘量多的匹配。
有些高級正則引擎支持惰性匹配,表示儘量少的匹配,只要能知足條件就當即中止。

  • *、    +、    ?、     {M,N} :都是貪婪匹配(greedy)
  • *?、  +?、  ??、   {M,N}? :都是惰性匹配(lazy,Reluctant)
  • *+、  ++、  ?+、   {M,N}+ :都是佔有優先匹配(possessive)

佔有優先和固化分組是相同的,只要佔有了就再也不交換,不容許進行回溯。示例見下面的(?>...)固化分組方式

12.匹配模式

  • (?i):不區分大小寫,可以使用(?-i)取消該模式。例如"(?i)abc(?-i)cdB"只對中間的abc進行不區分大小寫的匹配
    • 因爲(?i)遇到閉括號就失效,能夠將須要不區分大小寫匹配的部分寫入分組括號中,例如"((?i)abc)cdB",(?:(?i)abc)cdB=(?i:abc)cdB
  • (?x):extend模式,將忽略多個連續空格和註釋符到行尾的字符
  • (?m):(multiline)多行模式,改變^和$的匹配模式。默認模式下,它們匹配字符串首部和尾部。此模式下:
    • ^將匹配字符串首部和換行符。若要僅匹配字符串首部,使用\A。
    • $將匹配字符串尾部、換行符和換行符前的空字符。若要僅匹配字符串尾部和行尾,使用\Z,若要僅匹配字符串尾部,使用\z
  • (?s):(singleline或dotall)單行模式,改變"."的匹配模式,默認模式下,點"."沒法匹配換行符,dotall模式下能夠
  • (?U):lazy匹配模式。默認是greedy匹配。

13.強制字面解釋:\Q...\E。該序列將其中間的全部字符強制解釋爲字面符號,強制性極強。
但perl和pcre有所不一樣。perl中,該序列中間可引用變量進行變量替換,而pcre中變量符號也被看成普通字符。

14.普通分組和捕獲

  • (),$1,$2,$3,$4...有些地方使用\1,\2,\3,\4,sed中使用&表示全部匹配,perl中則使用$&
  • \g1,\g2,\g3或\g{1},\g{2},\g{3}。

其中$1,$2, ...用於正則外面,而"\g1", "\g2", ... 用於正則內部

15.命名分組和捕獲

  • (?:...):非命名捕獲,僅用於分組,不可用於引用,也稱爲非捕獲型括號。例如"(1|one)(?:2|two)(3|three)",$1=(1|one),$2=(3|three)
  • (?<NAME>...):命名捕獲,分組捕獲後還命名,就像變量賦值同樣。可使用\k<NAME>或\k'NAME'或\g{NAME}的方法來引用
  • (?>...):固化分組。一匹配成功就永不交回內容(用回溯的想法理解很容易)。

例如"hello world"能夠被"hel.* world"進行匹配,但不能被"hel(?>.*) world"匹配。
由於正常狀況下,".*"匹配到全部內容,而後回溯釋放已匹配內容直到空格" "字符。而固化分組後,已匹配的內容毫不交回,也就沒法回溯。

16. 重置匹配:\K 用於重置匹配的位置。
好比,foot\Kbar 匹配」footbar」,可是獲得的匹配結果是 」bar」。可是, \K 的使用不會干預到子組內的內容, 好比 (foot)\Kbar 匹配 」footbar」,第一個子組內的結果仍然會是 」foo」。

$ echo abc123abcfoo | grep -P -o '(abc)123\K\g1foo' 
abcfoo

17.要想對一個字符串匹配後取反。能夠經過正向環視錨定取反來間接實現。
例如,"-a -3 ac c 3 b"中取出負數、正數和空格很簡單,"-?[0-9]+|\s"便可,但想要藉此取反獲得"-a ac c b",目前正則表達式只能經過(?!)的環視取反實現:"((?!-?[0-9]+|\s).)*",外層括號表示右邊不是正數、負數或空格的字符都匹配並進行分組,而後重複量詞*,將連續的內容鏈接起來。
例如:

echo "-a -3 ac c 3 b" | grep -P '((?!-?[0-9]+|\s).)*'

...

相關文章
相關標籤/搜索