国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

30分鐘正則表達(dá)式指導(dǎo)

2024-07-21 02:29:48
字體:
供稿:網(wǎng)友

  by jim hollenhorst  譯 寒帶魚

  你是否曾經(jīng)想過正則表達(dá)式是什么,怎樣能夠快速得到對它的一個基本的認(rèn)識?我的目的就是在30分鐘內(nèi)帶你入門并且對正則表達(dá)式有一個基本的理解。事實是正則表達(dá)式并沒有它看起來那么復(fù)雜。學(xué)習(xí)它最好的辦法就是開始寫正則表達(dá)式并且不斷實踐。在最初的30分鐘之后,你就應(yīng)該知道一些基本的結(jié)構(gòu)并且有能力在你的程序或者web頁面中設(shè)計和使用正則表達(dá)式了。對那些想要深入研究的人,現(xiàn)在已經(jīng)有很多非常好的可用資源來讓你更深入的學(xué)習(xí)。

  到底什么是正則表達(dá)式?

  我相信你對模式匹配的“計算機通配符”字符應(yīng)該比較熟悉了。例如,如果你想要在一個windows文件夾中找到所有mircosoft word文件,你要搜索“*.doc”,因為你知道星號會被解釋為一個通配符,它匹配所有序列的字符串。正則表達(dá)式就是這種功能的一個更加細(xì)節(jié)的擴展。

  在寫處理文本的程序或者web頁面時,定位匹配復(fù)雜模式的字符串是很常見的。正則表達(dá)式就是用來描述這類模式的。這樣,一個正則表達(dá)式就是一個模式的縮減代碼。例如,模式“/w+”是表達(dá)“匹配任何包含字母數(shù)字字符的非空字符串”的精確方法。.net框架提供了一個功能強大類庫,它使得在你的應(yīng)用程序中包含正則表達(dá)式更加容易。使用這個庫,你可以輕易地搜索和替換文本,解碼復(fù)雜的標(biāo)題,解析語言,或者驗證文本。

  學(xué)習(xí)正則表達(dá)式的神秘的語法的一個好辦法是用例子作為開始學(xué)習(xí)的對象,然后實踐創(chuàng)建自己的正則表達(dá)式。

  讓我們開始吧!


  一些簡單的例子
 

  搜索elvis

  假設(shè)你要花費你所有的空余時間來掃描文檔來尋找elvis仍然活著的證據(jù)。你可以使用下面的正則表達(dá)式來搜索:

  1. elvis -- find elvis

  這是搜索精確字符序列的一個完全合法的正則表達(dá)式。在.net中,你可以輕松的設(shè)置選項來忽略字符的各種情況,所以這個表達(dá)式將會匹配“elivs”,“elvis”,或者“elvis”。不幸的是,它也將匹配單詞“pelvis”的后五個字母。我們可以改進(jìn)這個表達(dá)式如下:

  2. /belvis/b -- find elvis as a whole word

  現(xiàn)在事情變得更加有趣了。“/b”是一個特殊代碼,它表示“匹配任何單詞的開頭或結(jié)尾的位置”。這個表達(dá)式將只匹配完整的拼寫為“elvis”的單詞,無論是小寫的還是大寫的情況。

  假設(shè)你想要找到所有這樣的行,在其中單詞“elvis”后面都跟著單詞“alive”。句點或者點“.”是一個特殊代碼匹配除了換行符之外的任何字符。星號“*”表示重復(fù)前面的部分有必要的次數(shù)以保證能夠有一個匹配。這樣,“.*”表示“匹配除了換行符之外的任意數(shù)目的字符”。現(xiàn)在建立一個表示“搜索在同一行內(nèi)后面跟著單詞‘a(chǎn)live’的單詞‘elvis’”的表達(dá)式就是一件簡單的事了。

  3. /belvis/b.*/balive/b -- find text with "elvis" followed by "alive"

  僅僅使用幾個特殊字符我們就開始創(chuàng)建功能強大的正則表達(dá)式了,而且它們已經(jīng)開始變得難以被我們?nèi)祟惱斫饬恕?/p>

  讓我們看看另一個例子。

  確定電話號碼的合法性

  假設(shè)你的web頁面收集顧客的7位電話號碼,而且你希望驗證輸入的電話號碼是正確的格式,“xxx-xxxx”,這里每個“x”是一個數(shù)字。下面的表達(dá)式將搜索整個文本尋找這樣的一個字符串:

  4. /b/d/d/d-/d/d/d/d -- find seven-digit phone number
每個“/d”表示“匹配任何單個數(shù)字”。“-”沒有特殊的意義并且按照字面解釋,匹配一個連字符。要避免繁瑣的重復(fù),我們可以使用一個含有相同含義的速記符:

  5. /b/d{3}-/d{4} -- find seven-digit phone number a better way
“/d”后面的“{3}”表示“重復(fù)前面的字符三次”。

  .net正則表達(dá)式的基礎(chǔ)

  讓我們探索一下.net中正則表達(dá)式的基礎(chǔ)

  特殊字符

  你應(yīng)該知道幾個有特殊意義的字符。你已經(jīng)見過了“/b”,“.”,“*”,和“/d”。要匹配任何空白字符,像空格,制表符和換行符,使用“/s”。相似地,“/w”匹配任何字母數(shù)字字符。

  讓我們嘗試更多的例子:

  6. /ba/w*/b -- find words that start with the letter a

  這個搜索一個單詞的開頭(/b),然后是一個字母“a”,接著是任意次數(shù)重復(fù)的字母數(shù)字字符(/w*),最后是一個單詞的結(jié)尾(/b)。

  7. /d+ -- find repeated strings of digits

  這里,“+”與“*”是相似的,除了它需要至少一次重復(fù)。

  8. /b/w{6}/b -- find six letter words

  在expresso中測試這幾個表達(dá)式,然后實踐創(chuàng)建你自己的表達(dá)式。下面是一個說明有特殊含義的字符的表格:

匹配除換行符外的任何字符
/w 匹配任何字母數(shù)字字符
/s匹配任何空白字符
/d匹配任何數(shù)字
/b匹配一個單詞的開始或結(jié)尾
^匹配字符串的開始
$匹配字字符串的結(jié)尾

表1 正則表達(dá)式的常用特殊字符

  開始階段

  特殊字符“^”和“$”被用來搜索那些必須以一些文本開頭和(或)以一些文本結(jié)尾的文本。特別是在驗證輸入時特別有用,在這些驗證中,輸入的整個文本必須要匹配一個模式。例如,要驗證一個7位電話號碼,你可能要用:

  9. ^/d{3}-/d{4}$ -- validate a seven-digit phone number

  這是和第5個例子一樣的,但是強迫它符合整個文本字符串,匹配文本的頭尾之外沒有其他字符。通過在.net中設(shè)置“multiline”選項,“^”和“$”改變他們的意義為匹配一行文本的起點和結(jié)束,而不是整個正文字符串。expresso的例子使用這個選項。

  換碼字符

  當(dāng)你想要匹配這些特殊字符中的一個時會產(chǎn)生一個錯誤,像“^”或者“$”。使用反斜線符號來去掉它們的特殊意義。這樣,“/^”,“/.”,和“//”,分別匹配文本字符“^”,“.”,和“/”。

  重復(fù)

  你已經(jīng)見過了“{3}”和“*”可以指定一個單獨字符的重復(fù)次數(shù)。稍后,你會看到相同的語法怎樣用來重復(fù)整個子表達(dá)式。此外還有其他幾種方法來指定一個重復(fù),如下表所示:

*重復(fù)任意次數(shù)
+重復(fù)一次或多次
?重復(fù)一次或多次
{n}重復(fù)n次
{n,m}重復(fù)最少n次,最多m次
{n,}重復(fù)最少n次

表2 常用量詞

  讓我們試試幾個例子:

10. /b/w{5,6}/b -- find all five and six letter words

11. /b/d{3}/s/d{3}-/d{4} -- find ten digit phone numbers

12. /d{3}-/d{2}-/d{4} -- social security number

13. ^/w* -- the first word in the line or in the text

  在設(shè)置和不設(shè)置“multiline”選項的時試試最后一個例子,它改變了“^”的含義。

  字符集合

  搜索字母數(shù)字字符,數(shù)字,和空白字符是容易的,但如果你需要搜索一個字符集合中的任意字符時怎么辦?這可以通過在方括號中列出想要的字符來輕松的解決。這樣,“[aeiou]”就能匹配任意韻母,而“[.?!]”就匹配句子末尾的標(biāo)點。在這個例子中,注意“.”和“?”在方括號中都失去了他們的特殊意義而被解釋為文本含義。我們也可以指定一個范圍的字符,所以“[a-z0-9]”表示“匹配任何小寫字母或者任何數(shù)字”。

  讓我們試試一個搜索電話號碼的更加復(fù)雜的表達(dá)式:

14. /(?/d{3}[) ]/s?/d{3}[- ]/d{4} a ten digit phone number

  這個表達(dá)式將會搜索幾種格式的電話號碼,像“(800)325-3535”或者“650 555 1212”。“/(?”搜索0個或1個左圓括號,“[)]”搜索一個右圓括號或者一個空格。“/s?”搜索0個或一個空白字符。不幸的是,它也會找到像“650)555-1212”這樣括號沒有去掉的情況。在下面,你會看到怎樣用可選項解決這個問題。

  否定

  有些時候我們需要搜索一個字符,它不是一個很容易定義的字符集合的成員。下面的表格說明了這種字符怎樣指定:

/w匹配任何非字母數(shù)字字符
/s匹配任何非空白字符
/d匹配任何非數(shù)字字符
/b匹配非單詞開始或結(jié)束的位置
[^x]匹配任何非x字符
[^aeiou]匹配任何不在aeiou中的字符
            表3 怎樣指定你不想要東西

15. /s+ -- all strings that do not contain whitespace characters

  后面,我們會看到怎樣使用“l(fā)ookahead”和“l(fā)ookbehind”來搜索缺少更加復(fù)雜的模式的情況。

  可選項

  要從幾個可選項中選擇,允許符合任何一個的匹配,使用豎杠“|”來分隔可選項。例如,郵政編碼有兩種,一個是5位的,另一個是9位的加一個連字符。我們可以使用下面的表達(dá)式找到任何一種:

16. /b/d{5}-/d{4}/b|/b/d{5}/b -- five and nine digit zip codes

  當(dāng)使用可選項時,順序是很重要的因為匹配算法將試圖先匹配最左面的選擇。如果這個例子中的順序顛倒過來,表達(dá)式將只能找到5位的郵政編碼,而不會找到9位的。我們可以使用可選項來改進(jìn)十位電話號碼的表達(dá)式,允許包含區(qū)碼無論是通過空白字符還是連字符劃分的:

17. (/(/d{3}/)|/d{3})/s?/d{3}[- ]/d{4} -- ten digit phone numbers, a better way

  分組

  圓括號可以用來劃分一個子表達(dá)式來允許重復(fù)或者其他特殊的處理,例如:

18. (/d{1,3}/.){3}/d{1,3} -- a simple ip address finder

  表達(dá)式的第一部分搜索后面跟著一個“/.”的一個一位到三位的數(shù)字。這被放在圓括號中并且通過使用修飾符“{3}”被重復(fù)三次,后面跟著與之前一樣的表達(dá)式而不帶后綴部分。

  不幸的是,這個例子允許ip地址中被分隔的部分是任意的一位,兩位,或三位數(shù)字,盡管一個合法的ip地址不能有大于255的數(shù)字。要是能夠算術(shù)比較一個獲取的數(shù)字n使n<256就好了,但是只用正則表達(dá)式是不能夠辦到的。下一個例子使用模式匹配測試了基于第一位數(shù)字的多種可選項來保證限制數(shù)字的取值范圍。這表明一個表達(dá)式會變得很笨重,盡管搜索模式的描述是簡單的。

19. ((2[0-4]/d|25[0-5]|[01]?/d/d?)/.){3}(2[0-4]/d|25[0-5]|[01]?/d/d?) -- ip finder

  一個“回引”用來搜索前面被一個分組捕獲的已匹配文本的再現(xiàn)。例如,“/1”表示“匹配分組1中已捕獲到的文本”。下面是一個例子:

20. /b(/w+)/b/s*/1/b -- find repeated words

  它的運行過程是先捕獲一個分組1中“(/w+)”表示的至少包含一個字母數(shù)字字符的字符串,但僅當(dāng)它是一個單詞的開始或結(jié)束字符時才行。然后它搜索任意數(shù)量的空白字符“/s*”后跟以被捕獲的文本“/1”結(jié)尾的單詞。

  在上面的例子中,想要替換分組“(/w+)”這種寫法,我們可以把它寫成“(?<word>/w+)”來給這個分組命名為“word”。一個對這個分組的回引可以寫成“/k<word>”。試試下面的例子:

21. /b(?<word>/w+)/b/s*/k<word>/b -- capture repeated word in a named group

  通過使用圓括號,有很多可用的特殊用途的語法元素。一些最常用的歸納如下面這張表格:

捕獲
(exp)匹配exp并且在一個自動計數(shù)的分組中捕獲它
(?<name>exp)匹配exp并且在一個命名的分組中捕獲它
(?:exp)匹配exp并且不捕獲它
察看
(?=exp)匹配任何后綴exp之前的位置
(?<=exp)匹配任何前綴exp之后的位置
(?!exp)匹配任何未找到的后綴exp之后的位置
(?<!exp)匹配任何未找到的前綴exp之前的位置
評論
(?#comment)評論

表4 常用分組結(jié)構(gòu)

  前兩個我們已經(jīng)說過了。第三個“(?:exp)”不會改變匹配行為,它只是不像前兩個那樣捕獲已命名的或者計數(shù)的分組。

  確定察看(positive lookaround)

  下面四個是所謂的前向或后向斷言。它們從當(dāng)前的匹配向前或向后尋找需要的東西而不在匹配中包含它們。這些表達(dá)式匹配一個類似于“^”或“/b”的位置而不匹配任何文本,理解這個是很重要的。由于這個原因,他們也被稱為“零寬度斷言”。最好用例子來解釋它們:

  “(?=exp)”是“零寬度確定前向斷言”。它匹配一個文本中在給定后綴之前的位置,但不在匹配中包含這個后綴:

22. /b/w+(?=ing/b) -- the beginning of words ending with "ing"

  “(?<=exp)”是“零寬度確定后向斷言”。它匹配在給定前綴后面的位置,但不在匹配中包含這個前綴:

23. (?<=/bre)/w+/b -- the end of words starting with "re"

  下面這個例子可以用來重復(fù)向三位數(shù)為一組的數(shù)字中插入逗號的例子:

24. (?<=/d)/d{3}/b -- three digits at the end of a word, preceded by a digit

  下面是一個同時搜索前綴和后綴的例子:

25. (?<=/s)/w+(?=/s) -- alphanumeric strings bounded by whitespace

  否定察看(negative lookaround)

  之前,我說明了怎樣搜索一個不是特定字符或一個字符集合的成員的字符。那么如果我們想要簡單的驗證一個字符沒有出現(xiàn),但是不想匹配任何東西怎么辦?例如,如果我們想要搜索其中“q”不是后跟著“u”的單詞怎么辦?我們可以嘗試:

26. /b/w*q[^u]/w*/b -- words with "q" followed by not "u"

  運行例子你就會看到如果“q”是一個單詞的最后一個字母就不會匹配,比如“iraq”。這是因為“[^q]”總是匹配一個字符。如果“q”是單詞的最后一個字符,它會匹配后面跟著的空白字符,所以這個例子中表達(dá)式結(jié)束時匹配兩個完整的單詞。否定察看可以解決這個問題,因為它匹配一個位置而不消耗任何文本。與確定察看一樣,它也可以用來匹配一個任意復(fù)雜的子表達(dá)式的位置,而不僅僅是一個字符。我們現(xiàn)在可以做得更好:

27. /b/w*q(?!u)/w*/b -- search for words with "q" not followed by "u"

  我們使用“零寬度否定前向斷言”,“(?!exp)”,只有當(dāng)后綴“exp”沒有出現(xiàn)時它才成功。下面是另一個例子:

28. /d{3}(?!/d) -- three digits not followed by another digit

  相似地,我們可以使用“(?<!exp)”,“零寬度否定后向斷言”,來搜索文本中的一個位置,這里前綴“exp”沒有出現(xiàn):

29. (?<![a-z ])/w{7} -- strings of 7 alphanumerics not preceded by a letter or space

  這里是另一個使用后向的例子:

30. (?<=<(/w+)>).*(?=<///1>) -- text between html tags

  這個使用后向搜索一個html標(biāo)記,而使用前向搜索對應(yīng)的結(jié)束標(biāo)記,這樣,就能獲得中間的文本而不包括兩個標(biāo)記。

  評論

  標(biāo)點的另一個用法是使用“(?#comment)”語法包含評論。一個更好的辦法是設(shè)置“ignore pattern whitespace”選項,它允許空白字符插入表達(dá)式然后當(dāng)使用表達(dá)式時忽略它。設(shè)置了這個選項之后,任何文本每行末尾在數(shù)字符號“#”后面的東西都被忽略。例如,我們可以格式化先前的例子如下:

31. text between html tags, with comments

(?<=    # search for a prefix, but exclude it
  <(/w+)> # match a tag of alphanumerics within angle brackets
)          # end the prefix

.*        # match any text

(?=     # search for a suffix, but exclude it
  <///1>  # match the previously captured tag preceded by "/"
)         # end the suffix

  貪婪與懶惰

  當(dāng)一個正則表達(dá)式有一個可以接受一個重復(fù)次數(shù)范圍的量詞(像“.*”),正常的行為是匹配盡可能多的字符。考慮下面的正則表達(dá)式:

32. a.*b -- the longest string starting with a and ending with b

  如果這被用來搜索字符串“aabab”,它會匹配整個字符串“aabab”。這被稱為“貪婪”匹配。有些時候,我們更喜歡“懶惰”匹配,其中一個匹配使用發(fā)現(xiàn)的最小數(shù)目的重復(fù)。表2中所有的量詞可以增加一個問號“?”來轉(zhuǎn)換到“懶惰”量詞。這樣,“*?”的意思就是“匹配任何數(shù)目的匹配,但是使用達(dá)到一個成功匹配的最小數(shù)目的重復(fù)”。現(xiàn)在讓我們試試懶惰版本的例子(32):

33. a.*?b -- the shortest string starting with a and ending with b

  如果我們把這個應(yīng)用到相同的字符串“aabab”,它會先匹配“aab”然后匹配“ab”。

*?重復(fù)任意次數(shù),但盡可能少
+? 匹配一次或多次,但盡可能少
??重復(fù)零次或多次,但盡可能少
{n,m}?重復(fù)最少n次,但不多于m次,但盡可能少
{n,}?重復(fù)最少n次,但盡可能少

表5 懶惰量詞

  我們遺漏了什么?

  我已經(jīng)描述了很多元素,使用它們來開始創(chuàng)建正則表達(dá)式;但是我還遺漏了一些東西,它們在下面的表中歸納出來。這些中的很多都在項目文件中使用額外的例子說明了。例子編號在這個表的左列中列出。

/a報警字符
/b通常是單詞邊界,但是在一個字符集合中它表示退格鍵
/t 制表符
34/r回車
/v垂直制表符
/f分頁符
35/n換行符
/eesc
36/nnnascii碼八進(jìn)制數(shù)為nnn的字符
37/xnn十六進(jìn)制數(shù)為nn的字符
38/unnnn  unicode碼為nnnn的字符
39/c control n字符,例如回車(ctrl-m)就是/cm
40/a字符串的開始(像^但是不依賴于多行選項)
41/z字符串的結(jié)尾或者/n之前的字符串結(jié)尾(忽略多行)
/z 字符串結(jié)尾(忽略多行)
42/g當(dāng)前搜索的開始階段
43/p{name}命名為name的unicode類中的任何字符,例如/p{isgreek}
(?>exp)貪婪子表達(dá)式,也被稱為非回溯子表達(dá)式。它只匹配一次然后就不再參與回溯。
44(?<x>-<y>exp) or (?-<y>exp)balancing group. this is complicated but powerful. it allows named capture groups to be manipulated on a push down/pop up stack and can be used, for example, to search for matching parentheses, which is otherwise not possible with regular expressions. see the example in the project file.
45(?im-nsx:exp)正則表達(dá)式選項為子表達(dá)式exp
46(?im-nsx)change the regular expression options for the rest of the enclosing group
(?(exp)yes|no)the subexpression exp is treated as a zero-width positive lookahead. if it matches at this point, the subexpression yes becomes the next match, otherwise no is used.
(?(exp)yes)same as above but with an empty no expression
(?(name)yes|no)this is the same syntax as the preceding case. if name is a valid group name, the yes expression is matched if the named group had a successful match, otherwise the no expression is matched.
47 (?(name)yes)same as above but with an empty no expression

表6 我們遺漏的東西。左端的列顯示了項目文件中說明這個結(jié)構(gòu)的例子的序號

  結(jié)論

  我們已經(jīng)給出了很多例子來說明.net正則表達(dá)式的關(guān)鍵特性,強調(diào)使用工具(如expresso)來測試,實踐,然后是用例子來學(xué)習(xí)。如果你想要深入的研究,網(wǎng)上也有很多在線資源會幫助你更深入的學(xué)習(xí)。你可以從訪問ultrapico網(wǎng)站開始。如果你想讀一本相關(guān)書籍,我建議jeffrey friedl寫的最新版的《mastering regular expressions》。

code project中還有很多不錯的文章,其中包含下面的教程:

·an introduction to regular expressions by uwe keim
·microsoft visual c# .net developer's cookbook: chapter on strings and regular expressions

注:本文例子可以從ultrapico網(wǎng)站下載expresso測試,點這里下載該程序,點這里察看原文。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 饶阳县| 类乌齐县| 兴仁县| 兖州市| 南开区| 枣庄市| 河津市| 渝北区| 类乌齐县| 万山特区| 汶上县| 前郭尔| 沙洋县| 静宁县| 涿鹿县| 巴中市| 景宁| 台中市| 汉寿县| 观塘区| 通城县| 广安市| 廉江市| 文昌市| 水富县| 蓝田县| 延津县| 晴隆县| 新乡县| 福鼎市| 永清县| 南京市| 林州市| 临高县| 克东县| 青州市| 海南省| 大竹县| 拉孜县| 杭锦后旗| 宁阳县|