## 23.正則表達式
你和你的女朋友在一家珠寶店里在一起,她選擇了最好的鉆石戒指之一,并給了你這一切......肯定你知道自己會陷入更多的信用卡債務中。 你正處于另一天,正與你的老板交談,以待他盯著你時獲得更多薪水。 那個表情。 你現在知道,無論你說過對公司做得如何,都將被忽略并且徒勞無功。 我們都看到別人臉上的表情,并嘗試預測接下來的表情。
假設你正在和朋友聊天,他輸入:-),就知道他很高興,這是:-(表示他很傷心。這很明顯,我們甚至可以在文本數據中看到表達式,就像我們在每個文本中看到的一樣 Ruby 的正則表達式為你提供了一種檢測給定文本中的模式并根據需要提取它們的方法,這可用于一些有用的事情,本章就是關于這一點的。Ruby 并沒有告訴你 如何在不背負債務的情況下打動你的女孩:-(。報告為錯誤,并希望他們在 ruby 的下一個主要版本中修復它;-)
啟動你的 irb,開始吧。
### 23.1。 創建一個空的正則表達式
好的,我們將嘗試創建一個空的正則表達式。 在終端中輸入`irb -–simple-prompt`,然后輸入以下內容(不顯示`>>`,這是 irb 的提示)
```rb
>> //.class
=> Regexp
```
你會看到`//.class`是`Regexp`。 換句話說,在那些`/`和`/`之間的任何事物都是正則表達式 <sup class="footnote">[ [52](#_footnotedef_52 "View footnote.") ]</sup> 。 正則表達式不是字符串,而是表示模式。 任何字符串都可以匹配或不匹配模式。
### 23.2。 檢測圖案
現在讓我們看看如何檢測正則表達式中的模式。 假設你要查看`abc`是否在給定的字符串中。 只需打以下代碼
```rb
>> /abc/.match "This string contains abc"
=> #<MatchData "abc">
```
給定的字符串中存在`abc`,因此你將獲得一個匹配項。 在下面的代碼段中,字符串中沒有`abc`,因此它返回`nil`。
```rb
>> /abc/.match "This string contains cba"
=> nil
```
你可以在正則表達式上使用`match`函數,如上所示,也可以在字符串上使用它,如下所示。 兩者都給你相同的結果。
```rb
>> "This string contains abc".match(/abc/)
=> #<MatchData "abc">
```
匹配的另一種方式如下所示。 你可以使用`=~`運算符。
```rb
>> "This string contains abc" =~ /abc/
=> 21
>> /abc/ =~ "This string doesn't have abc"
=> 25
```
`=~`會告訴你第一次出現匹配項的位置。
如果你想簡單得多,即只想知道是否存在匹配項,則可以使用`match?``,它返回`true`或`false`,如下所示
```rb
>> /abc/.match? "This string contains abc"
=> true
>> /abc/.match? "This string contains def"
=> false
```
#### 23.2.1。 要記住的事情
你需要記住一些事情,或者至少不時提及。 下表中提到了這些內容。 <sup class="footnote">[ [53](#_footnotedef_53 "View footnote.") ]</sup> 。
<colgroup><col style="width: 50%;"> <col style="width: 50%;"></colgroup>
| 事情 | 這是什么意思 |
| 。 | 任何單個字符 |
| \ w | 任何文字字符(字母,數字,下劃線) |
| \ W | 任何非文字字元 |
| \ d | 任何數字 |
| \ D | 任何非數字 |
| \ s | 任何空白字符 |
| \ S | 任何非空白字符 |
| \ b | 任何單詞邊界字符 |
| ^ | 行首 |
| $ | 行結束 |
| \一種 | 字符串開始 |
| \ z | 字符串結尾 |
| [abc] | 一個字符 |
| a,b 或 c | [^ abc] |
| 除單個字符外 | a,b 或 c |
| [a-z] | a-z 范圍內的任何單個字符 |
| [a-zA-Z] | a-z 或 A-Z 范圍內的任何單個字符 |
| (...) | 捕獲所有封閉的內容 |
| (a | b) | a 或 b |
| 一種? | 零或一個 |
| 一種* | 零個或多個 |
| a + | 一個或多個 |
| 一個{3} | 恰好是 3 個 |
| a {3,} | 3 個或更多 |
| 一個{3,6} | 在 3 到 6 之間 |
| 一世 | 不區分大小寫 |
| 米 | 使點匹配換行符 |
| X | 忽略正則表達式中的空格 |
| ? | 僅執行一次#{…}替換 |
如果你不理解,請不要驚慌,你會追上來。
### 23.3。 點
正則表達式中的點匹配任何事物。 為了說明這一點,讓我們在 irb 中嘗試一些示例
```rb
>> /.at/.match "There is rat in my house"
=> #<MatchData "rat">
```
在上面的代碼片段中,我們嘗試將`/.at/`與一個字符串匹配,并且匹配。 原因是,該字符串包含一個名為`rat`的單詞。 再看下面的兩個示例,`/.at/`匹配`cat`和`bat`,沒有大驚小怪。
```rb
>> /.at/.match "There is cat in my house"
=> #<MatchData "cat">
>> /.at/.match "There is bat in my house"
=> #<MatchData "bat">
```
將點放在單詞或其他內容的開頭不是規則,它可以在任何位置。 正則表達式`/f.y/`可以輕松匹配`fry`和`fly`。 啊! 我希望我現在有一個炸雞。 雞會飛。
### 23.4。 角色類
假設我們想發現給定的弦中有蝙蝠,老鼠或貓。 如果它在那里,我們將打印出房子里有動物,否則我們將不會打印任何東西。 你可能會認為我們需要使用三個正則表達式,例如`/bat/`,`/cat/`和`/rat/`,但事實并非如此。 從這三個正則表達式中我們知道,只有第一個字符會變化。 那么`/.at/`像上一個一樣。 嗯,這也不起作用,因為它將匹配“吃”,“墊子”,“脂肪”等單詞。
因此,這次嚴格來說,我們只想匹配蝙蝠,老鼠和貓,因此我們提出了這樣的正則表達式:`/[bcr]at/`,它將僅匹配這三個動物單詞,而沒有其他匹配。 沖壓以下示例,然后在計算機上運行
```rb
#!/usr/bin/ruby
# regexp_character_classes.rb
puts "There is a animal in your house" if /[bcr]at/.match "There is bat in my house"
puts "There is a animal in your house" if /[bcr]at/.match "There is rat in my house"
puts "There is a animal in your house" if /[bcr]at/.match "There is cat in my house"
puts "There is a animal in your house" if /[bcr]at/.match "There is mat in my house"
```
輸出量
```rb
There is a animal in your house
There is a animal in your house
There is a animal in your house
```
從上面的輸出中可以看到,字符串“房子里有動物”會三次打印,而不是第四次匹配失敗,因為“房子里有墊子”。
字符類也可以接受范圍。 輸入以下代碼并運行它。
```rb
#!/usr/bin/ruby
# regexp_character_classes_1.rb
print "Enter a short string: "
string = gets.chop
puts "The string contains character(s) from a to z" if /[a-z]/.match string
puts "The string contains character(s) from A to Z" if /[A-Z]/.match string
puts "The string contains number(s) from 0 to 9" if /[0-9]/.match string
puts "The string contains vowels" if /[aeiou]/.match string
puts "The string contains character(s) other than a to z" if /[^a-z]/.match string
puts "The string contains character(s) other than A to Z" if /[^A-Z]/.match string
puts "The string contains number(s) other than 0 to 9" if /[^0-9]/.match string
puts "The string contains characters other than vowels" if /[^aeiou]/.match string
```
Output
```rb
Enter a short string: fly
The string contains character(s) from a to z
The string contains character(s) other than A to Z
The string contains number(s) other than 0 to 9
The string contains characters other than vowels
```
輸出 1
```rb
Enter a short string: Burgundy 32
The string contains character(s) from a to z
The string contains character(s) from A to Z
The string contains number(s) from 0 to 9
The string contains vowels
The string contains character(s) other than a to z
The string contains character(s) other than A to Z
The string contains number(s) other than 0 to 9
The string contains characters other than vowels
```
好的,你從輸出中推斷出什么? 當你進行飛行時,這些正則表達式的匹配項:
* / [a-z] /,因為它包含從 a 到 z 的字符
* / [^ A-Z] /因為它包含從 A 到 Z 的任何地方都不屬于的字符,因此你會在捕獲內知道^表示否定。 ^還有其他用途,如果我不偷懶的話,你會寫它。
* / [^ 0-9] /,因為它不包含 0 到 9 之間的任何數字
* / [^ aeiou] /,因為它不包含元音(a 或 e 或 i 或 o 或 u)
據此,將打印`puts`語句中的消息。 現在看輸出 1,我給程序一個字符串`Burgundy 27`,檢查你的假設/邏輯是否與之吻合。
### 23.5。 掃描
我喜歡 String Class 中的`scan`方法。 它使我們可以在大量字符串中搜索某些內容。 就像大海撈針一樣,由于計算機的速度越來越快,你可以進行越來越多的掃描。 它們非常適合搜索。 他們很安靜,與印度的警察不同,后者只有在被盜的人行賄的情況下才會進行搜查。
因此,在下面的程序中打孔。 它掃描字符串中的單詞。
```rb
#!/usr/bin/ruby
# regexp_scan.rb
string = """ There are some words in this string and this program will
scan those words and tell their word count """
words = string.scan(/\w+/)
puts "The words are:"
p words
puts # prints a empty line
puts "there are #{words.count} words in the string"
puts "there are #{words.uniq.count} unique words in string"
```
Output
```rb
The words are:
["There", "are", "some", "words", "in", "this", "string", "and", "this", "program", "will", "scan", "those", "words", "and", "tell", "their", "word", "count"]
there are 19 words in the string
there are 16 unique words in string
```
注意`/\w+/`,這是什么意思? 請參閱此表[要記住的事情](#_things_to_remember)。 你可以看到`\w`表示任何字符,例如字母,數字,下劃線,而`+`表示一個或多個。 換句話說,我假設單詞由任何字母,數字和下劃線組合組成,并且一個單詞至少包含一個或多個字母。 因此,語句`string.scan(/\w+/)`將掃描所有單詞并將其放入一個稱為 word 的變量中,我們將在此程序中使用該變量。
語句`p words`打印出數組,并在下一行
```rb
puts "there are #{words.count} words in the string"
```
我們使用命令`word.count`計算數組字中的元素數量,并使用`#{words.count}`將其嵌入字符串中并輸出給用戶。
在下一條語句中
```rb
puts "there are #{words.uniq.count} unique words in string"
```
我們使用`words.uniq.count`查找給定字符串中有多少個獨特的單詞,并將其也打印給用戶。 例如,如果你掃描一本由兩位作者組成的大書,并將其輸入該程序,則可以認為擁有更多獨特單詞的人的詞匯量更高。
現在讓我們看看另一個程序。 例如,使用一條推文,你將在 Twitter 上進行操作,并且你想確定該推文是否包含 Twitter 用戶名。 因此,現在讓我們分析 Twitter 用戶名的構造,它首先包含一個@符號,后跟一個單詞。 換句話說,它必須與 regexp `/@\w+/`相匹配。 在以下程序中,我們掃描推文中提到的所有用戶
```rb
#!/usr/bin/ruby
# regexp_scan_twitter_users.rb
string = """ There is a person @karthik_ak who wrote ilr. Its about a
language called @ruby invented by @yukihiro_matz """
users = string.scan(/@\w+/)
puts "The users are:"
p users
```
Output
```rb
The users are:
["@karthik_ak", "@ruby", "@yukihiro_matz"]
```
注意上面程序中的`string.scan(/@\w+/)`。 它掃描以`@`符號開頭的所有單詞,收集它們并將它們作為數組返回,最后我們使用`p users`顯示該數組。
### 23.6。 捕獲
我們已經看到了正則表達式可能很有用。 現在,假設我們找到了一個帶有正則表達式的匹配項,而只想捕獲其中的一小部分,例如在電子郵件中輸入用戶名,或者在某些生日中輸入一個月,那么該怎么做?
為此,我們使用圓括號并將其稱為捕獲。 下面是一個程序,該程序詢問一個人的生日并從中提取月份。
```rb
#!/usr/bin/ruby
# regexp_capture.rb
print "Enter Birthday (YYYY-MM-DD) :"
date = gets.chop
/\d{4}-(\d{2})-\d{2}/.match date
puts "You were born on month: #{$1}"
```
Output
```rb
Enter Birthday (YYYY-MM-DD) :1982-11-22
You were born on month: 11
```
注意此行`/\d{4}-(\d{2})-\d{2}/.match` date,在這里我們檢查日期是否與以下內容匹配:也就是說,它必須有四個數字`/\d{4}/`,然后必須跟一個連字符`/\d{4}-/`,然后必須跟兩個數字`/\d{4}-\d(2}/`,并且必須在其后加上連字符和另外兩位數字`/\d{4}-\d{2}-\d{2}/`。
現在我們只需要捕獲中間的月份。 因此,我們像`/\d{4}-(\d{2})-\d{2}/`所示那樣將花括號括起來,將這個正則表達式粘貼在上面的程序的這一行中
```rb
/\d{4}-(\d{2})-\d{2}/.match date
```
現在,此捕獲`(\d{2})`存儲在哪里? 它被存儲在全局變量`$1`中,如果有另一個捕獲,則將其存儲在另一個變量`$2`和`$3`中,依此類推……。所以我們現在知道`$1`有月份,我們使用 在下面的行中打印出結果
```rb
puts "You were born on month: #{$1}"
```
在下面的示例 [regexp_capture_1.rb](code/regexp_capture_1.rb) 中,我們嘗試了三個捕獲,我們希望一次性捕獲年,月和日。 因此,我們使用以下正則表達式`/(\d{4})-(\d{2})-(\d{2})/`。 在下面鍵入程序并執行。
```rb
#!/usr/bin/ruby
# regexp_capture_1.rb
print "Enter Birthday (YYYY-MM-DD) :"
date = gets.chop
/(\d{4})-(\d{2})-(\d{2})/.match date
puts "Year: #{$1} \n Month: #{$2} \n Date: #{$3}"
```
Output
```rb
Enter Birthday (YYYY-DD-MM) :1997-12-67
Year: 1997
Month: 12
Date: 67
```
這里,從左開始的第一個捕獲存儲在`$1`中,第二個捕獲存儲在`$2`中,第三個捕獲在`$3`中,依此類推(如果我們給出了更多的信息)。 如果你想知道`$0`是什么,為什么不在程序末尾給出`puts $0`語句并查看會發生什么呢?
在下一個程序中,我們將其設計為可以容忍用戶輸入中的某些錯誤。 用戶可能不會總是給出 1990-04-17,他可能會給出 1990 1990 年 4 月 17 日或類似的數字,在數字之間可能會有空格。 輸入 int 程序并執行
```rb
#!/usr/bin/ruby
# regexp_capture_2.rb
print "Enter Birthday (YYYY-MM-DD) :"
date = gets.chop
/\s*(\d{4})\s*-\s*(\d{2})\s*-\s*(\d{2})\s*/.match date
puts "Year: #{$1} \n Month: #{$2} \n Date: #{$3}"
```
Output
```rb
Enter Birthday (YYYY-MM-DD) :1947- 07 - 14
Year: 1947
Month: 07
Date: 14
```
如你所見,該程序將查找月份,日期和年份! 如果你注意到我們正在使用`/\s*(\d{4})\s*-\s*(\d{2})\s*-\s*(\d{2})\s*/`的正則表達式,我們用`\s*`填充了數字,那又是什么呢? 再次參考 regexp 表[要記住的事情](#_things_to_remember)。 `\s`表示空格, **表示零或多個,所以說`\s`** `\d{4}`表示匹配,以使 regexp 具有 4 位數字并以零個或多個空格開頭,并且[ `\s*\d{4}\s*`匹配一個 4 位數字,該數字前面有零個或多個空格。 因此,無論你給空格多少填充,它都會找出日期。
### 23.7。 嵌套捕獲
嵌套捕獲是捕獲內的捕獲。 輸入以下代碼并在 irb 中執行:
```rb
>> /(a(c(b)))/.match "This sting contains acb so it has match"
=> #<MatchData "acb" 1:"acb" 2:"cb" 3:"b">
>> $1
=> "acb"
>> $2
=> "cb"
>> $3
=> "b"
```
請參閱 regexp `/(a(c(b)))/`,這對你有意義嗎? 那么如何閱讀呢? 首先卸下所有括號,然后像`/acb/`一樣閱讀。 `acb`存在于字符串中,因此與之匹配,因此我們得到了輸出的一部分,如下所示
```rb
=> #<MatchData "acb"....
```
現在從左開始應用最外面的括號,我們得到一個捕獲,如`/(acb)/`所示,它匹配并捕獲了`acb`,它在輸出中顯示為`1:"acb"`部分,如下所示
```rb
=> #<MatchData "acb" 1:"acb" 2:"cb" 3:"b">
```
此捕獲存儲在`$1`全局變量中。 現在忘了外括號,從左移到第二對括號,你將得到以下正則表達式`/a(cb)/`,它與`acb`匹配并捕獲字符串中的`cb`,它被捕獲在變量`$2`中,并且也 在下面的匹配數據的`2:"cb"`部分中顯示
```rb
=> #<MatchData "acb" 1:"acb" 2:"cb" 3:"b">
```
以最類似的方式,最里面的括號對形成正則表達式/ ac(b)/,并且其在變量`$3`中的捕獲顯示在下面的匹配輸出`3:"b"`中
```rb
=> #<MatchData "acb" 1:"acb" 2:"cb" 3:"b">
```
### 23.8。 MatchData 類
因此,你已經匹配了東西并捕獲了它。 好吧,如果你在 irb 中注意到了,無論何時與某事物匹配,都會返回一個對象。 一切都是 Ruby 中的對象,但這不是 String 類型的對象,而是一個名為`MatchData`的新類型。 因此,讓我們玩一下它,看看它能做什么。 因此,請參見下面的示例,其中將正則表達式`/abc/`與字符串匹配,并將其存儲在變量`m`中
```rb
>> m = /abc/.match "This stuff has abc and something after it"
=> #<MatchData "abc">
```
要查看匹配的內容,我們給命令`m.match`并拋出錯誤,如下所示!
```rb
>> m.match
NoMethodError: undefined method 'match' for #<MatchData "abc">
from (irb):2
from /home/webtoday/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in '<main>'
```
那么如何獲得比賽? 好吧,看起來 MatchData 是一個數組,其中第一個元素是匹配的文本,因此鍵入`m[0]`以獲取匹配的數據,如下所示
```rb
>> m[0]
=> "abc"
```
有時你可能會對比賽前后的情況感興趣。 `pre_match`函數獲取匹配之前的一段文本,如下所示
```rb
>> m.pre_match
=> "This stuff has "
```
像`pre_match`一樣,`post_match`則相反,它獲取比賽后出現的一段文字。
```rb
>> m.post_match
=> " and something after it"
```
如果要查看天氣,則可以捕獲任何捕獲信息,可以如圖所示調用捕獲函數。
```rb
>> m.captures
=> []
```
當然,這次你沒有捕獲,因此 captures 函數返回一個空數組。
好了,關于 MatchData 對象中的捕獲,請看下面的代碼。 我們為正則表達式提供了`/((ab)c)/`之類的捕獲功能。 字符串`"This stuff has abc and something after it"`中的此正則表達式將與`abc`匹配,并將捕獲`abc`和`ab`(如果你已理解上一節中的捕獲內容)。 好了,如何在 MatchData 對象中獲取捕獲,首先讓我們將正則表達式與字符串匹配,并將其存儲在變量 m 中,如下所示
```rb
>> m = /((ab)c)/.match "This stuff has abc and something after it"
=> #<MatchData "abc" 1:"abc" 2:"ab">
```
現在要查看捕獲,使用 MatchData 對象上的捕獲功能,如下所示
```rb
>> m.captures
=> ["abc", "ab"]
```
因此,你獲得的捕獲為 Array,可以將其視為 Array 對象。 你也可以直接從 MatchData 獲取捕獲,如下所示,MatchData 數組中向前的第二個元素存儲可以由`m[1]`,`m[2]`……`m[n]`訪問的捕獲,如下所示
```rb
>> m[1]
=> "abc"
>> m[2]
=> "ab"
```
好吧,我告訴過`m`屬于 MatchData 類,并且還沒有提供證明。 就這個
```rb
>> m.class
=> MatchData
```
### 23.9。 錨和斷言
#### 23.9.1。 錨點
錨是 Ruby 中的參考點。 假設我們要檢查一行是否立即以= begin <sup class="footnote">[ [54](#_footnotedef_54 "View footnote.") ]</sup> 開頭,那么我可以使用類似`/^=begin/`的正則表達式對其進行檢查,其中`^`符號是代表行首的錨點:
```rb
/^=begin/.match "=begin"
=> #<MatchData "=begin">
```
假設我們有多個行字符串,并且想要提取一行(第一個)。 所以第一行的內容可以是任何東西,因此我們得到一個正則表達式為`/.+/`,現在它必須在行`^`的開始和行`$`的結尾之間,因此我們得到的正則表達式如圖`/^.+$/` ],此正則表達式將匹配行錨之間的任何內容。 一個例子如下所示。
```rb
>> /^.+$/.match "Boom \n Thata"
=> #<MatchData "Boom ">
```
在上面的示例中,請注意`\n`將字符串分成兩行,因為`\n`代表換行符。 因此,正則表達式忠實地匹配第一行內容,即`“Boom ”`。
我們擁有的下一種錨點是`\A`代表字符串的開始和`\z`代表字符串的結束。 在下面的示例中,我們使用以下 regexp `/\ASomething/`檢查字符串是否以某些東西開頭
```rb
>> /\ASomething/.match "Something is better than nothing"
=> #<MatchData "Something">
```
我們得到了一場比賽。 在下面的示例中,我們得到 nil 匹配,因為字符串不是以 Something 開頭。
```rb
>> /\ASomething/.match "Everybody says Something is better than nothing"
=> nil
```
現在讓我們檢查字符串的末尾是否緊隨其后,因此我們形成了一個正則表達式,如`/nothing\z/`所示
```rb
>> /nothing\z/.match "Everybody says Something is better than nothing"
=> #<MatchData "nothing">
```
不出所料,我們一無所獲。 應該注意的是,錨點不會反映在比賽數據中,錨點不是字符,而是位置的符號表示。 因此,如果你期望與`nothing\z`匹配,請忽略它。
```rb
>> /nothing\z/.match "Everybody says Something is better than nothing\n"
=> nil
```
看上面的例子,`\z`匹配一個沒有行尾(`\n`)字符的字符串。 如果要檢查行尾,則必須使用大寫`Z`,如下所示
```rb
>> /nothing\Z/.match "Everybody says Something is better than nothing\n"
=> #<MatchData "nothing">
```
如此搭配!
在下面的示例中,我們匹配以\ n 作為結尾的字符串中的所有內容。
```rb
>> /\A.+\Z/.match "Everybody says Something is better than nothing\n"
=> #<MatchData "Everybody says Something is better than nothing">
```
#### 23.9.2。 斷言
假設你正在尋找這個人 David Copperfield。 你的名稱目錄很大,你想匹配他的名字。 我們可以使用斷言 <sup class="footnote">[ [55](#_footnotedef_55 "View footnote.") ]</sup> 進行匹配。 因此,假設你要搜索 Copperfield 之前的內容,為此我們使用前瞻性斷言。 請看下面的例子
```rb
>> /\w+\s+(?=Copperfield)/.match "David Copperfield"
=> #<MatchData "David ">
```
看一下`(?=Copperfield)`,這就是它的期待,這次是`Copperfield`。 想要很快致富嗎? 然后搜索`(?=Goldfield`)并想要一些好音樂,然后搜索(?= Oldfield) <sup class="footnote">[ [56](#_footnotedef_56 "View footnote.") ]</sup> 。
這就是事情,無論你在`(?=`和`)`之間給出什么,都將很期待,如果之前有任何內容,它將得到匹配。 因此在科波菲爾之前有大衛,因此被匹配了。 請注意,我給了`\w+\s+`,這意味著我想匹配一個或多個字母的正則表達式,然后匹配一個或多個以 Copperfield 開頭的空格。 因此,這里有另一個示例,給出了正匹配:
```rb
>> /\w+\s+(?=Copperfield)/.match "Joan Copperfield"
=> #<MatchData "Joan ">
```
假設我們要匹配所有不以 Copperfield 結尾的名稱,我們將使用前瞻性否定斷言。 為此,我們將 Copperfield 放在`(?!`和`)`之間,因此在以下示例中,它將返回負匹配
```rb
>> /\w+\s+(?!Copperfield)/.match "Joan Copperfield"
=> nil
```
但是在下一個示例中,它將返回正匹配項,因為 Joan 不在 Copperfield 之前
```rb
>> /\w+\s+(?!Copperfield)/.match "Joan Kansamy"
=> #<MatchData "Joan ">
```
我們已經看到了期待的斷言。 現在讓我們看一下回溯斷言。 假設我們要匹配姓戴維的人的姓氏。 然后,我們可以從姓氏向后看,看其是否為 David。 簽出下面的代碼
```rb
>> /(?<=David)\s+\w+/.match "Joan Kansamy"
=> nil
```
參見上面的代碼。 我們將 David 放在`(??`和`)`之間,因此你可以指定回溯斷言。 上面的代碼返回 nil,因為其中沒有 David。
```rb
>> /(?<=David)\s+\w+/.match "David Munsamy"
=> #<MatchData " Munsamy">
```
上面的示例匹配`“ Munsamy”`<sup class="footnote">[[57](#_footnotedef_57 "View footnote.")]</sup> because we have David before the pattern `\s+\w+`
就像我們對負面的期望一樣,為什么我們不能向后看呢? 只需將`=`替換為`!`,你將獲得否定的向后看斷言。 因此,以下示例將不匹配,因為你在 Munsamy 前面有 David。
```rb
>> /(?<!David)\s+\w+/.match "David Munsamy"
=> nil
```
現在以下面的示例為例,我們將得到一個匹配項,因為第一個`\s+\w+`前面沒有 David,也就是說,在下面的示例中是`“in”`后跟一個空格。
```rb
>> /(?<!David)\s+\w+/.match "All in all Munsamy"
=> #<MatchData " in">
```
### 23.10。 忽略個案
好吧,讓我們說說這些電子郵件 [mindaslab@protonmail.com](mailto:mindaslab@protonmail.com) 和 [MINDASLAB@PROTONMAIL.COM](mailto:MINDASLAB@PROTONMAIL.COM) 之間的區別是什么,什么都沒有,兩個地址都向我發送郵件,所以如果我們要掃描 特定電子郵件的字符串,我們希望忽略大小寫。 所以考慮下面的例子
```rb
>> /abc/i.match "There is ABC in string"
=> #<MatchData "ABC">
```
在上面的示例中,我們有一個正則表達式`/abc/`,但它匹配給定字符串中的 ABC。 如果你注意到了,你可能會看到我在正則表達式后放置了`i`,`i`代表忽略大小寫。 好了,請看下面的示例,`i`無條件匹配任何內容,并不關心大小寫。
```rb
>> /abc/i.match "There is AbC in string"
=> #<MatchData "AbC">
```
### 23.11。 忽略空間
就像`i`一樣,`x`也應在正則表達式的后面使用。 它忽略 regexp 中的空格并匹配字符串。 見下面的例子
```rb
>> /ab c/x.match "There is abc in string"
=> #<MatchData "abc">
```
但這并不意味著它會忽略匹配字符串中的空格,在下面的示例中,我們有一個正則表達式`/ab c/`(ab 空間 c),但與字符串中的`ab c`(ab 空間 c)不匹配! 這可能令人驚訝。 這意味著在附加 x 時,它意味著將從 regexp 中刪除所有空格。
```rb
>> /ab c/x.match "There is ab c in string"
=> nil
```
### 23.12。 動態正則表達式
我們可能需要動態創建 Regexp,比如說我想根據獲取的用戶數據創建搜索查詢。 看一下下面的程序,在文本編輯器中鍵入并運行它
```rb
# regexp_dynamic.rb
Friends = [
"Bharath - Looks like alien",
"Nithya - The MBA. Oh NOOOOOO",
"Tat - The eskimo from Antartica",
"Kannan - Eats lot of bondas",
"Karthik - Loves briyani"
]
print "Enter search term: "
term = gets.chop
regexp = Regexp.new term
searched_friends = Friends.collect{|f| f if f.match regexp}.compact
puts searched_friends.join "\n"
```
Output
```rb
Enter search term: The
Nithya - The MBA. Oh NOOOOOO
Tat - The eskimo from Antartica
```
在代碼中,我們首先聲明一個名為`Friends`的數組,其中包含有關我們朋友的數據,如下所示
```rb
Friends = [
"Bharath - Looks like alien",
"Nithya - The MBA. Oh NOOOOOO",
"Tat - The eskimo from Antartica",
"Kannan - Eats lot of bondas",
"Karthik - Loves briyani"
]
```
因此,讓我們分析代碼。 在接下來的兩行中(如下所示),我將獲得搜索詞并將其分配給變量`term`
```rb
print "Enter search term: "
term = gets.chop
```
接下來仔細看下一行
```rb
regexp = Regexp.new term
```
看一下`Regexp.new term`部分,這是所有奇跡發生的地方。 現在打開 irb 并輸入以下內容
```rb
>> Regexp.new "something"
=> /something/
```
因此,如你所見,當你給`Regexp.new`提供字符串時,它將轉換為 Regexp。 你可以進行如下所示的高級操作
```rb
>> r = Regexp.new "(\\d+)\\s+oranges"
=> /(\d+)\s+oranges/
```
因此,在`Regexp.new term`中,我們將`term`轉換為正則表達式。 現在,我們需要做的就是使用此正則表達式,并在以下代碼`searched_friends = Friends.collect{|f| f if f.match regexp}.compact`中選擇與之匹配的字符串
我們在以下代碼中打印數組
```rb
puts searched_friends.join "\n"
```
看看我在這里編寫的簡單計算器程序 [https://raw.githubusercontent.com/mindaslab/ilrx/master/x/calculator.rb](https://raw.githubusercontent.com/mindaslab/ilrx/master/x/calculator.rb) ,對于新手來說可能很復雜,所以不要 不用擔心,如果你聽不懂。
- 前言
- 紅寶石
- 先決條件
- 1.安裝 Ruby
- 2.在線資源
- 3.入門
- 4.比較與邏輯
- 5.循環
- 6.數組
- 7.哈希和符號
- 8.范圍
- 9.功能
- 10.可變范圍
- 11.類&對象
- 12.安全導航
- 13.打破大型程序
- 14.結構和 OpenStruct
- 15. Rdoc
- 16. Ruby 樣式指南
- 17.模塊和混入
- 18.日期和時間
- 19.文件
- 20. Proc,Lambda 和塊
- 21.多線程
- 22.異常處理
- 23.正則表達式
- 24.寶石
- 25.元編程
- 26.基準
- 27.測試驅動開發
- 28.觀察者模式
- 29.模板模式
- 30.工廠模式
- 31.裝飾圖案
- 32.適配器模式
- 33.單例模式
- 34.復合模式
- 35.建造者模式
- 36.策略模式
- 贊助商
- 捐
- 人們怎么說
- 版權
- 取得這本書