ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 23.正则表达式 你和你的女朋友在一家珠宝店里在一起,她选择了最好的钻石戒指之一,并给了你这一切......肯定你知道自己会陷入更多的信用卡债务中。 你正处于另一天,正与你的老板交谈,以待他盯着你时获得更多薪水。 那个表情。 你现在知道,无论你说过对公司做得如何,都将被忽略并且徒劳无功。 我们都看到别人脸上的表情,并尝试预测接下来的表情。 假设你正在和朋友聊天,他输入:-),就知道他很高兴,这是:-(表示他很伤心。这很明显,我们甚至可以在文本数据中看到表达式,就像我们在每个文本中看到的一样 Ruby 的正则表达式为你提供了一种检测给定文本中的模式并根据需要提取它们的方法,这可用于一些有用的事情,本章就是关于这一点的。Ruby 并没有告诉你 如何在不背负债务的情况下打动你的女孩:-(。报告为错误,并希望他们在 ruby 的下一个主要版本中修复它;-) 启动你的 irb,开始吧。 ### 23.1。 创建一个空的正则表达式 好的,我们将尝试创建一个空的正则表达式。 在终端中输入`irb -–simple-prompt`,然后输入以下内容(不显示`&gt;&gt;`,这是 irb 的提示) ```rb >> //.class => Regexp ``` 你会看到`//.class`是`Regexp`。 换句话说,在那些`/`和`/`之间的任何事物都是正则表达式 &lt;sup class="footnote"&gt;[ [52](#_footnotedef_52 "View footnote.") ]&lt;/sup&gt; 。 正则表达式不是字符串,而是表示模式。 任何字符串都可以匹配或不匹配模式。 ### 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。 要记住的事情 你需要记住一些事情,或者至少不时提及。 下表中提到了这些内容。 &lt;sup class="footnote"&gt;[ [53](#_footnotedef_53 "View footnote.") ]&lt;/sup&gt; 。 <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 &#124; 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 &lt;sup class="footnote"&gt;[ [54](#_footnotedef_54 "View footnote.") ]&lt;/sup&gt; 开头,那么我可以使用类似`/^=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。 你的名称目录很大,你想匹配他的名字。 我们可以使用断言 &lt;sup class="footnote"&gt;[ [55](#_footnotedef_55 "View footnote.") ]&lt;/sup&gt; 进行匹配。 因此,假设你要搜索 Copperfield 之前的内容,为此我们使用前瞻性断言。 请看下面的例子 ```rb >> /\w+\s+(?=Copperfield)/.match "David Copperfield" => #<MatchData "David "> ``` 看一下`(?=Copperfield)`,这就是它的期待,这次是`Copperfield`。 想要很快致富吗? 然后搜索`(?=Goldfield`)并想要一些好音乐,然后搜索(?= Oldfield) &lt;sup class="footnote"&gt;[ [56](#_footnotedef_56 "View footnote.") ]&lt;/sup&gt; 。 这就是事情,无论你在`(?=`和`)`之间给出什么,都将很期待,如果之前有任何内容,它将得到匹配。 因此在科波菲尔之前有大卫,因此被匹配了。 请注意,我给了`\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”`&lt;sup class="footnote"&gt;[[57](#_footnotedef_57 "View footnote.")]&lt;/sup&gt; 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) ,对于新手来说可能很复杂,所以不要 不用担心,如果你听不懂。