JavaRush /Java Blog /Random-TW /RegEx:掌握正規表示式的 20 個簡短步驟。第三部分
Artur
等級 40
Tallinn

RegEx:掌握正規表示式的 20 個簡短步驟。第三部分

在 Random-TW 群組發布
RegEx:掌握正規表示式的 20 個簡短步驟。第 1 部分 :正規表示式:掌握正規表示式的 20 個簡短步驟。第 2 部分: 在這一部分中,我們將繼續討論稍微複雜一點的事情。但像以前一樣掌握它們並不困難。我再說一遍,正規表示式實際上比乍看起來更容易,您不需要成為火箭科學家就可以掌握它並開始在實踐中使用它。本文的英文原文在這裡掌握正規表示式的 20 個簡短步驟。 第 3 - 1 部分

第 11 步:用括號()作為捕獲組

掌握正規表示式的 20 個簡短步驟。 第 3 - 2 部分在上一個問題中,我們尋找不同類型的整數值和浮點(點)數值。但正規表示式引擎並不會區分這兩種類型的值,因為所有內容都被捕獲在一個大的正規表示式中。如果我們將迷你模式括在括號中,我們可以告訴正規表示式引擎區分不同類型的匹配:
模式:([AZ])|([az])
字串:  玻利維亞現任總統是 Evo Morales。
配對:^^^ ^^^^^^^ ^^^^^^^^^ ^^ ^^^^^^^ ^^ ^^^ ^^^^^^^
組:    122 222222 122222222 22 1222222 22 122 1222222  
範例)上面的正規表示式定義了兩個從 1 開始索引的捕獲組。第一個捕獲組匹配任何單個大寫字母,第二個捕獲組匹配任何單個小寫字母。透過使用“或”號|和括號()作為捕獲組,我們可以定義匹配多種字串的單一正規表示式。如果我們將其應用於本文前一部分中的長/浮點搜尋正規表示式,則正規表示式引擎將捕獲適當群組中的相應匹配項。透過檢查子字串匹配哪一組,我們可以立即確定它是浮點值還是長整數值:
模式:(\d*\.\d+[fF]|\d+\.\d*[fF]|\d+[fF])|(\d+[lL])字串: 42L 
12   x 3.4f 6l 3.3 0F LF .2F 0。
配對:^^^ ^^^^ ^^ ^^ ^^^
組:    222 1111 22 11 111  
範例)這個正規表示式非常複雜,為了更好地理解它,讓我們將其分解並查看每個模式:
( // 符合任何「float」子字串
  \d*\.\d+[fF]
  |
  \d+\.\d*[fF]
  |
  \d+[fF]
)
| // 或者
( // 符合任何「長」子字串
  \d+[lL]
)
括號中的符號|和捕獲組()允許我們匹配不同類型的子字串。在這種情況下,我們匹配浮點數“float”或長整數“長”。
(
  \d*\.\d+[fF] // 小數點右邊 1+ 位
  |
  \d+\.\d*[fF] // 小數點左邊 1+ 位
  |
  \d+[fF] // 沒有點,只有 1+ 位數
)
|
(
  \d+[lL] // 沒有點,只有 1+ 位數
)
在「float」捕獲組中,我們有三個選項:小數點右側至少有 1 位的數字、小數點左側至少有 1 位的數字以及沒有小數點的數字。只要它們末尾附加有字母“f”或“F”,它們中的任何一個都是“浮點數”。在“長”捕獲組中,我們只有一個選項 - 我們必須有 1 個或多個數字,後跟字元“l”或“L”。正規表示式引擎將在給定字串中查找這些子字串並將它們索引到適當的捕獲組中。 筆記我們不匹配任何沒有添加“l”、“L”、“f”或“F”的數字。這些數字應該如何分類?好吧,如果它們有小數點,Java 語言預設為“double”。否則它們必須是“int”。

讓我們透過幾個謎題來鞏固我們所學到的知識:

在上面的正規表示式中新增兩個捕獲組,以便它也可以對 double 或 int 數字進行分類。(這是另一個棘手的問題,如果需要一段時間也不要氣餒,作為最後的手段請參閱我的解決方案。)
圖案:
字串:   42L 12 x 3.4f 6l 3.3 0F LF .2F 0。
符合:^^^ ^^ ^^^^ ^^ ^^^ ^^ ^^^ ^^
組:    333 44 1111 33 222 11 111 22
)下一個問題稍微簡單。使用括號內的捕獲組()、「或」號|和字元範圍對以下年齡進行排序:「在美國合法飲酒」。(>= 21) 和「不允許在美國飲酒」(<21):
圖案:
字串:   7 10 17 18 19 20 21 22 23 24 30 40 100 120
符合:^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^^ ^^^
組:    2 22 22 22 22 22 11 11 11 11 11 11 111 111 
解決方案

第 12 步:首先確定更具體的匹配

掌握正規表示式的 20 個簡短步驟。 第 3 - 3 部分如果您嘗試將「合法飲酒者」定義為第一個捕獲組而不是第二個,那麼您在上一個任務中可能會遇到一些麻煩。為了理解原因,讓我們來看另一個例子。假設我們要分別記錄包含少於4個字元的姓氏和包含4個或更多字元的姓氏。讓我們為第一個捕獲組指定較短的名稱,看看會發生什麼:
模式:([AZ][az]?[az]?)|([AZ][az][az][az]+)
字串:   Kim Job s Xu Clo yd Moh r Ngo Roc k。
配對:^^^ ^^^ ^^ ^^^ ^^^ ^^^ ^^^
群:    111 111 11 111 111 111 111   
範例)預設情況下,大多數正規表示式引擎對我們迄今為止看到的基本字元使用貪婪匹配。這意味著正規表示式引擎將捕獲在提供的正規表示式中儘早定義的最長組。因此,雖然上面的第二組可以捕獲名稱中的更多字符,例如“Jobs”和“Cloyd”,但由於這些名稱的前三個字符已經被第一個捕獲組捕獲,因此第二個捕獲組無法再次捕獲它們。現在讓我們做一個小修正 - 只需更改捕獲組的順序,將更具體(更長)的組放在前面:
模式:([AZ][az][az][az]+)|([AZ][az]?[az]?)
字串:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
配對:^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
組:    222 1111 22 11111 1111 222 1111    

任務...這次只有一個:)

「更具體」的模式幾乎總是意味著「更長」。假設我們想要找出兩個「單字」:首先是那些以元音開頭的單字(更具體地說),然後是那些不以元音開頭的單字(任何其他單字)。嘗試編寫正規表示式來捕獲和識別與這兩組相符的字串。(下面的組是用字母表示的,而不是編號的。您必須確定哪個組應對應於第一個組,哪個組應對應於第二組。)
圖案:
字串:   pds6f uub 24r2gp ewqrty l ui_op
符合:^^^^^ ^^^ ^^^^^^ ^^^^^^ ^ ^^^^^
組:    NNNNN VVV NNNNNN VVVVVVV N VVVVV
解決方案) 一般來說,正規表示式越精確,它的長度就越長。它越準確,您捕獲不需要的東西的可能性就越小。因此,雖然它們可能看起來很可怕,但更長的正規表示式〜=更好的正則表達式。很遺憾

步驟 13:花括號{}表示特定的重複次數

掌握正規表示式的 20 個簡短步驟。 第 3 - 4 部分在上一個步驟中姓氏的範例中,我們在一個模式中有 2 個幾乎重複的群組:
模式:([AZ][az][az][az]+)|([AZ][az]?[az]?)
字串:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
配對:^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
組:    222 1111 22 11111 1111 222 1111    
對於第一組,我們需要包含四個或更多字母的姓氏。第二組必須捕獲包含三個或更少字母的姓氏。[a-z]有沒有比一遍又一遍地重複這些組別更簡單的寫法呢?如果為此使用花括號則存在{}。大括號{}允許我們指定前一個字元或捕獲組的最小和(可選)最大匹配數。有以下三個用例{}
{X} // 剛好符合 X 次
{X,} // 匹配 >= X 次
{X,Y} // 符合 >= X 且 <= Y 次
以下是這三種不同語法的範例:
模式:[az]{11}
字串:   humuhumunuk unukuapua'a。
配對:^^^^^^^^^^^   
模式:[az]{18,}
字串:   humuhumunukunukuapua 'a.
符合:^^^^^^^^^^^^^^^^^^^^^^    
模式:[az]{11,18}
字串:   humuhumunukunukuap ua'a。
符合:^^^^^^^^^^^^^^^^^^^    
範例)上面的範例有幾點要注意。筆記:。首先,使用 {X} 表示法,前一個字元或群組將精確匹配該數字 (X) 次。如果「單字」中有更多字元(多於數字 X)可以匹配模式(如第一個範例所示),那麼它們將不會包含在匹配中。如果字元數小於 X,則完全匹配將失敗(嘗試將第一個範例中的 11 變更為 99)。其次,符號{X,}和{X,Y}是貪婪的。他們將嘗試匹配盡可能多的字符,同時仍然滿足給定的正則表達式。如果指定 {3,7},則可以匹配 3 到 7 個字符,如果接下來的 7 個字符有效,則將匹配所有 7 個字符。如果指定 {1,} 並且接下來的 14,000 個字元全部匹配,則所有 14,000 個字元都將包含在相應的字串中。我們如何利用這些知識來重寫上面的表達式?最簡單的改進可能是將相鄰組替換[a-z][a-z]{N},其中 N 是相應選擇的:
模式:([AZ][az]{2}[az]+)|([AZ][az]?[az]?)  
……但這並沒有讓事情變得更好。查看第一個捕獲組:我們有[a-z]{2}(與 2 個小寫字母精確匹配),後面跟著[a-z]+(與 1 個或多個小寫字母相符)。我們可以透過使用大括號要求 3 個或更多小寫字母來簡化:
模式:([AZ][az]{3,})|([AZ][az]?[az]?) 
第二個捕獲組是不同的。這些姓氏中不需要超過三個字符,這意味著我們有上限,但下限為零:
模式:([AZ][az]{3,})|([AZ][az]{0,2}) 
使用正規表示式時,特異性總是更好,因此明智的做法是就此停止,但我忍不住注意到這兩個相鄰的字元範圍 ([AZ][az]) 看起來幾乎像一個“單字字元”類,\w( [A-Za-z0-9_]) 。如果我們確信我們的資料只包含格式良好的姓氏,那麼我們可以簡化正規表示式並簡單地編寫:
模式:(\w{4,})|(\w{1,3}) 
第一組捕捉 4 個或更多「單字字元」( [A-Za-z0-9_]) 的任何序列,第二組擷取 1 到 3 個「單字字元」(含)的任何序列。這行得通嗎?
模式:(\w{4,})|(\w{1,3})
字串:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
配對:^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
組:    222 1111 22 11111 1111 222 1111    
範例)成功了!這種方法怎麼樣?而且它比我們之前的範例乾淨得多。由於第一個捕獲組與具有四個或更多字元的所有姓氏相匹配,我們甚至可以將第二個捕獲組更改為 simple \w+,因為這將允許我們捕獲所有剩餘的姓氏(具有1、2 或3個字元):
模式:(\w{4,})|(\w+)
字串:   Kim Jobs Xu Cloyd Mohr Ngo Rock。
配對:^^^ ^^^^ ^^ ^^^^^ ^^^^ ^^^ ^^^^
組:    222 1111 22 11111 1111 222 1111    

讓我們幫助大腦學習這一點並解決以下兩個問題:

使用大括號{}重寫步驟 7 中的社會安全號碼找出正規表示式:
圖案:
字串:113-25=1902 182-82-0192 H23-_3-9982 1I1-O0-E38B
配對:              ^^^^^^^^^^^
() 假設某網站的密碼強度檢查器要求使用者密碼長度在6到12個字元之間。編寫一個正規表示式來標記下面列表中的無效密碼。每個密碼都包含在括號中()以便於匹配,因此請確保正規表示式以文字()符號字元開頭和結尾。提示:請確保密碼中不允許使用帶有[^()]或類似的文字括號,否則最終將匹配整個字串!
圖案:
字串:   (12345)(我的密碼)(Xanadu.2112)(su_do)(OfSalesmen!)
配對:^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
解決方案

第14步:\b零寬度邊框符號

掌握正規表示式的 20 個簡短步驟。 第 3 - 5 部分最後的任務相當困難。""但是,如果我們透過將密碼放在引號而不是括號中來使其變得更複雜一點呢()?我們可以透過簡單地將所有括號字元替換為引號字元來編寫類似的解決方案嗎?
模式:\"[^"]{0.5}\"|\"[^"]+\s[^"]*\"
字串:   "12345" "我的密碼" "Xanadu.2112 " " su_do" " OfSalesmen! ”
配對:^^^^^^^ ^^^^^^^^^^^^^ ^^^ ^^^  
範例)結果並不令人印象深刻。你已經猜到原因了嗎?問題是我們在這裡尋找不正確的密碼。「Xanadu.2112」是一個很好的密碼,因此當正規表示式意識到該序列不包含空格或文字字元時,它會在右側限定密碼的"字元之前產生。(因為我們指定使用 無法在密碼中找到"字元。)一旦正則表達式引擎確信這些字元與特定正則表達式不匹配,它就會再次運行,恰好在它停止的位置 - 字元所在的位置。這限制了「世外桃源.2112」在右邊。從那裡他看到一個空格字元和另一個字元- 對他來說這是錯誤的密碼!基本上,他找到了這個序列並繼續前進。這根本不是我們想要得到的......如果我們能夠指定密碼的第一個字元不應該空格,那就太好了。有沒有辦法做到這一點?(到目前為止,您可能已經意識到我所有反問句的答案都是“是”。) 是的!有這樣的辦法!許多正規表示式引擎提供轉義序列,例如“字邊界” 。「字邊界」是一個零寬度轉義序列,奇怪的是,它與字邊界相符。請記住,當我們說“單字”時,我們指的是類別中的任何字元序列或. 單字邊界匹配意味著緊鄰序列之前或之後的字元必須是單字字元。但是,在匹配時,我們不會將該字元包含在捕獲的子字串中。這是零寬度。為了了解它是如何運作的,讓我們來看一個小例子: "[^"]""" "\b\b\w[A-Za-z0-9_]\bне
模式:\b[^]+\b
字串:   Ve still vant ze money , Lebowski。
配對:^^ ^^^^^ ^^^^ ^^ ^^^^^ ^^^^^^^^  
範例)序列[^ ]必須符合非文字空格字元的任何字元。那麼為什麼這不匹配,Money 後面的逗號或.Lebowski 後面的句號 " 呢?這是因為逗號,和句號.不是單詞字符,所以在單詞字符和非單詞字符之間創建了界限。它們出現y在“money”一詞,及其後的逗號。以及「 iLebowski 一詞及其.後的句號(句號/句號)之間。正規表示式符合這些單字的邊界(但不匹配僅幫助定義它們的非單字字元)。\b但是,如果我們的模板中 不包含一致性,會發生什麼情況?
模式:[^]+
字串:   Ve still vant ze money,Lebowski。
符合:^^ ^^^^^ ^^^^ ^^ ^^^^^^ ^^^^^^^^^  
範例)是的,現在我們也找到了這些標點符號。現在讓我們使用單字邊界來修復帶有引號的密碼的正規表示式:
模式:\"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\"
字串:   "12345" "我的密碼" " Xanadu。第
配對:^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^  
)透過將單字邊界放在引號內(“\b ... \b”),我們實際上是在說匹配密碼的第一個和最後一個字元必須是“單字字元”。因此,這裡工作正常,但如果用戶密碼的第一個或最後一個字符不是單字字符,則效果不佳:
模式:\"\b[^"]{0.5}\b\"|\"\b[^"]+\s[^"]*\b\"
字串:“thefollowingpasswordistooshort”“C++”
火柴:   
範例)看看第二個密碼如何沒有被標記為“無效”,即使它顯然太短。你一定是小心與序列\b,因為它們只匹配字元之間的邊界\w,而不匹配\w。在上面的範例中,由於我們在密碼中允許使用 not , 字符\w,因此密碼的第一個/最後一個字符之間的邊界\不能保證是單字邊界\b

為了完成這一步驟,我們將只解決一個簡單的問題:

當我們想要匹配特定的字元序列,但想要確保它們只出現在單字的開頭或結尾(或單獨出現)時,單字邊界在語法突出顯示引擎中非常有用。假設我們正在編寫語法突出顯示並希望突出顯示單字 var,但僅當它單獨出現時(不觸及單字中的其他字元)。你能為此寫一個正規表示式嗎?當然可以,這是一個非常簡單的任務;)
圖案:
字串:   var varx _var ( var j) barvarcar * var var -> { var }
符合:^^^ ^^^ ^^^ ^^^ ^^^  
解決方案

第 15 步:“插入符號”^作為“行的開頭”,美元符號$作為“行的結尾”

掌握正規表示式的 20 個簡短步驟。 第 3 - 6 部分單字邊界序列\b(來自本文上一部分的最後一步)並不是唯一可在正規表示式中使用的特殊零寬度序列。最受歡迎的兩個是“插入符號” ^-“行的開頭”和美元符號$-“行的結尾”。在正規表示式中包含其中之一意味著匹​​配必須出現在來源字串的開頭或結尾:
模式:^start|end$
字串:   start end start end start end start end
匹配:^^^^^ ^^^  
範例)如果您的字串包含換行符,它將^start匹配任何行開頭的序列“start”,並將end$匹配任何行末尾的序列“end”(儘管這很難在此處顯示)。這些符號在處理包含分隔符號的資料時特別有用。^讓我們使用「行首」回到步驟 9 中的「檔案大小」問題。在此範例中,我們的檔案大小由空格“”分隔 。因此,我們希望每個檔案大小以數字開頭,前面是空格字元或行開頭:
模式:(^| )(\d+|\d+\.\d+)[KMGT]B
字串:   6.6KB 1..3KB 12KB 5G 3.3MB KB .6.2TB 9MB。
配對:^^^^^ ^^^^^ ^^^^^^ ^^^^
組:    222 122 1222 12    
)我們已經離目標很近了!但您可能會注意到,我們仍然有一個小問題:我們在有效檔案大小之前會匹配空格字元。現在,當我們的正規表示式引擎找到此捕獲組 (1) 時,我們可以簡單地忽略它,或者我們可以使用非捕獲組,我們將在下一步中看到。

同時,讓我們解決另外 2 個語氣問題:

繼續上一步的語法突出顯示範例,某些語法突出顯示將標記尾隨空格,即非空白字元和行尾之間的任何空格。你能寫一個正規表示式來僅突出顯示尾隨空格嗎?
圖案:
字串:myvec <- c(1, 2, 3, 4, 5)  
配對:                         ^^^^^^^  
解決方案)一個簡單的逗號分隔值(CSV)解析器將尋找用逗號分隔的「標記」。一般來說,除非用引號括起來,否則空格沒有任何意義""。寫一個簡單的 CSV 解析正規表示式,該表達式匹配逗號之間的標記,但忽略(不捕獲)不在引號之間的空格。
圖案:
字串:   a, "b", "c d",e,f, "g h", dfgi,, k, "", l
符合:^^ ^^^^ ^^^^^^^^^^ ^^ ^ ^^^ ^^^^^^ ^^ ^^^ ^
群組:    21 2221 2222212121 222221 222211 21 221 2    
解決方案RegEx:20個簡短步驟掌握正規表示式。第 4 部分。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION