D-12, Ruby 正规表达式(二) 量词 、锚 && Reverse Vowels of a String

昨天的重点复习/./就是一个最简单的正规表达式。

先认识一下match=~

match回传匹配的内容。
=~回传匹配的位置。

2.7.3 :195 > /abc/.match("hello abc here abc")
 => #<MatchData "abc">
2.7.3 :196 > /abc/.match?("hello abc here abc")
 => true
#把检查对象内符合表达式的内容,回传出来。

2.7.3 :197 > /abc/ =~ "hello abc here abc"
 => 6
2.7.3 :198 > /bca/ =~ "hello abc here abc"
 => nil
2.7.3 :199 > /./ =~ "hello abc here abc"
 => 0
2.7.3 :204 > "hello abc here abc"[6]
 => "a"
#回传符合的位置的index,当然0才是第一个。如果检查对象内有两个,只回传第一个。

分组()

当需要检测或找出的资料有两组的例子。

2.7.3 :006 > /(Rails).*(easy)/.match("Do you like Rails? It so easy!")
 => #<MatchData "Rails? It so easy" 1:"Rails" 2:"easy">

.*先暴雷,*代表有0或多个。

2.7.3 :007 > $1
 => "Rails"
2.7.3 :008 > $2
 => "easy"

可以看到用$可以呼叫出匹配到的资料,有n组就可以到$n


开始认识那些鬼画符。

那些符号

+?.*^$()[]{ }|\
以上都代表有特殊功能,.是代表任何一个字前面示范已充足展示,如果当想表达这些符号出来,都是利用\来处理。

2.7.3 :009 > /(\?).*(easy)/.match("Do you like Rails? It so easy!")
 => #<MatchData "? It so easy" 1:"?" 2:"easy">
 
 2.7.3 :010 > /(?).*(easy)/.match("Do you like Rails? It so easy!")
Traceback (most recent call last): undefined group option: /(?).*(easy)/ (SyntaxError)

2.7.3 :011 > /..\?../.match("Do you like Rails? It so easy!")
 => #<MatchData "ls? I">

量词,重复的次数。

*:0或多次。
+:1或多次。 两兄弟,一起示范。

2.7.3 :013 > /.*\?/.match("?")
 => #<MatchData "?">

2.7.3 :014 > /.*\?/.match("Date?")
 => #<MatchData "Date?">

2.7.3 :016 > /.+\?/.match("Date?")
 => #<MatchData "Date?">
 
2.7.3 :017 > /.+\?/.match("?")
 => nil
#如果?号前面没有字元出现过至少一个就抓不到了。

{n}:指定n次。

2.7.3 :047 > /.*/.match("hello world")
 => #<MatchData "hello world">
 
2.7.3 :048 > /.{5}/.match("hello world")
 => #<MatchData "hello">
 
2.7.3 :049 > /.{3}/.match("hello world")
 => #<MatchData "hel">

{n,}:n次或更多。

2.7.3 :053 > /a{3,}/.match("a")
 => nil
 
2.7.3 :054 > /a{3,}/.match("aaa")
 => #<MatchData "aaa">
 
2.7.3 :055 > /a{3,}/.match("aaaaaaaaa")
 => #<MatchData "aaaaaaaaa">

{,m}:m次或更少次。

2.7.3 :056 > /a{,3}/.match("aaaaaaaaa")
 => #<MatchData "aaa">
 
2.7.3 :057 > /a{,3}/.match("aa")
 => #<MatchData "aa">
 
2.7.3 :058 > /a{,3}/.match("a")
 => #<MatchData "a">

{n,m}:最少n次,最多m次。

2.7.3 :059 > /a{2,3}/.match("a")
 => nil
 
2.7.3 :060 > /a{2,3}/.match("aa")
 => #<MatchData "aa">
 
2.7.3 :061 > /a{2,3}/.match("aaaaa")
 => #<MatchData "aaa">

?:本身为0或1次,常与其他语法搭配。简单一点理解,量词加上?之後以执行最少次优先(非贪婪)。

2.7.3 :068 > /a?/.match?("cde")
 => true  #0次匹配到也ok
 
2.7.3 :069 > /a/.match?("cde")
 => false
 
2.7.3 :062 > /a{3,}?/.match("aaaaaaaaa")
 => #<MatchData "aaa">
 
2.7.3 :063 > /a{3,}/.match("aaaaaaaaa")
 => #<MatchData "aaaaaaaaa">

量词都以贪婪优先,尽可能去匹配,但加上?就是非贪婪,另外+在量词後也有另外的效果。

/a.*b/假设有一个表达式是这样,当我们去match``"a123b123123"时,会先抓到a後,会开始匹配.*,而整段字串都符合.*,所以其实会先记忆住a123b123123,而当继续匹配b时,被记忆住的a123b123123,会3``2``1``3``2``1这样释放出来到b为止。
而当量词加上+後,会不允许这样的行为,将a123b123123记忆住不回朔。

整理一下

`*`:0或多次。
`+`:1或多次。
`?`:0或1次。
`{n}`:指定n次。
`{n,}`:n次或更多。
`{,m}`:m次或更少次。
`{n,m}`:最少n次,最多m次。

网路上看到一些资料写re+re*,想半天也想不出这是什麽,其实就是量词。
RERegular Expression的缩写,re*等於是指一段regexp重复0或多次。

锚(指定位置)符号

^:匹配於行首。
$:匹配於行尾。

2.7.3 :111 > /^ruby/.match("gogo ruby, fight ruby")
 => nil
 
2.7.3 :112 > /^ruby/.match("ruby gogo , fight ruby")
 => #<MatchData "ruby">
#不是在行首就匹配不到。

2.7.3 :115 > /ruby$/.match("ruby gogo , fight ruby")
 => #<MatchData "ruby">
 
2.7.3 :116 > /ruby$/.match("ruby gogo , ruby fight")
 => nil
#记得$在後。

\A:匹配开头"字串",不包括\n後的位置。
\Z:匹配结尾"字串"。可以是\n前的位置,也可以是绝对结尾。
\z:匹配结尾"字串",允许包括\n,即字符串的绝对结尾。
看code比较快

2.7.3 :194 > "\nruby love you".match(/^ruby/)
 => #<MatchData "ruby">
 
2.7.3 :195 > "\nruby love you".match(/\Aruby/)
 => nil
 
2.7.3 :196 > "ruby love you".match(/\Aruby/)
 => #<MatchData "ruby">
#\A与^感觉上\A更精准,有绝对行首的意思在。

2.7.3 :210 > "you love ruby\n".match(/ruby$/)
 => #<MatchData "ruby">
 
2.7.3 :211 > "you love \nruby".match(/ruby$/)
 => #<MatchData "ruby">
 
2.7.3 :212 > "you love ruby\n".match(/ruby\Z/)
 => #<MatchData "ruby">
 
2.7.3 :213 > "you love \nruby".match(/ruby\Z/)
 => #<MatchData "ruby">
 
2.7.3 :214 > "you love \nruby".match(/ruby\z/)
 => #<MatchData "ruby">
 
2.7.3 :215 > "you love ruby\n".match(/ruby\z/)
 => nil

#\Z与$ 没有差太多,\z明显更精准。

这边先不要太去care分别,用多了自然会知道该用哪种。

\b:明确抓出单词时用。
\B:抓出单词,但一边不用明确时用。
感觉自己不像在讲国语!!!

2.7.3 :230 > /ruby/.match("iloverubyloveyou")
 => #<MatchData "ruby">
2.7.3 :231 > /\bruby\b/.match("iloverubyloveyou")
 => nil
2.7.3 :232 > /\bruby\b/.match("ilove ruby loveyou")
 => #<MatchData "ruby">
#单字的"边界"

2.7.3 :233 > /\bruby\B/.match("rubyloveyou")
 => #<MatchData "ruby">
2.7.3 :234 > /\Bruby\b/.match("rubyloveyou")
 => nil
2.7.3 :235 > /\Bruby\b/.match("iloveruby")
 => #<MatchData "ruby">

2.7.3 :236 > /\Bruby\B/.match("iloverubyloveyou")
 => #<MatchData "ruby">
#没意义

整理

`^`:匹配於行首。
`$`:匹配於行尾。
`\A`:匹配开头"字串",不包括`\n`後的位置。
`\Z`:匹配结尾"字串"。可以是`\n`前的位置,也可以是绝对结尾。
`\z`:匹配结尾"字串",允许包括`\n`,即字符串的绝对结尾。
`\b`:这样记,明确抓出单词时用。
`\B`:抓出单词,但一边不用明确时用。

讲到这又篇幅过大了...

今天再认识一些特殊用法

/(?=!)/:有一个!才匹配。

2.7.3 :244 > /ruby(?=!)/.match("ruby")
 => nil
2.7.3 :245 > /ruby(?=!)/.match("ruby!")
 => #<MatchData "ruby">
2.7.3 :246 > /ruby(?=!)/.match("iloveruby")
 => nil
2.7.3 :247 > /ruby(?=!)/.match("iloveruby!")
 => #<MatchData "ruby">

/(?!!)/:没有!才匹配。

2.7.3 :248 > /ruby(?!!)/.match("iloveruby!")
 => nil
2.7.3 :249 > /ruby(?!!)/.match("iloveruby")
 => #<MatchData "ruby">

(?i):R後面的资料不分大小写。

2.7.3 :253 > /R(?i)uby/.match("RUBY")
 => #<MatchData "RUBY">
2.7.3 :254 > /R(?i)uby/.match("Ruby")
 => #<MatchData "Ruby">
2.7.3 :255 > /Ruby/.match("RUBY")
 => nil

/bbb|aaa/:or(给易拼错字的人用)。

2.7.3 :256 > /model|modle/.match("rails g modle Rail")
 => #<MatchData "modle">
2.7.3 :257 > /model|modle/.match("rails g modle Rail model")
 => #<MatchData "modle">

/mod(le|el):内部or(给易拼错字的人用)。

2.7.3 :258 > /mod(el|le)/.match("rails g modle Rail")
 => #<MatchData "modle" 1:"le">
#用|会纪录最先抓到的。

好像突然知道,为何Rails常有提醒我打错字了?

/(!+|\?)/:匹配有多!个或一个?

2.7.3 :267 > /ruby(!+|\?)/.match("ruby!!!!! ruby?")
 => #<MatchData "ruby!!!!!" 1:"!!!!!">

明天就可以开始解决任何原本看不懂的天书了o 0!


今天的leetcode345. Reverse Vowels of a String
题目连结:https://leetcode.com/problems/reverse-vowels-of-a-string/
题目重点:又是一题用正规表达式会很好解的。

gsub有四种用法,除了像昨天输入两个参数後,会把字串里指定的值(第一个参数),换成第二个参数。

2.7.3 :017 > "hello".gsub("l", "a")
 => "heaao"

还有只带一个参数的话,可以成为枚举器(迭代器)。

2.7.3 :017 > "hello".gsub("l", "a")
 => "heaao"
2.7.3 :018 > "hello".gsub("l")
 => #<Enumerator: ...>
2.7.3 :019 > "hello".gsub("l") {puts "抓到几个,替换几个"}
抓到几个,替换几个
抓到几个,替换几个
 => "heo"
 
2.7.3 :020 > "hello".gsub("l") { "a" }
 => "heaao"

因为这题要我们将aeiou的所在位置reverse

abci => ibca
abeci => ibeca

那我们先将母音的值与顺序抓出来。

2.7.3 :021 > "hello".scan(/[aeiou]/i)
 => ["e", "o"]

如果gsub(/[aeiou]/i) { }block里可以将["e", "o"]由後一一提供的话,那就可以替换完成了。
pop

2.7.3 :022 > x = ["a", "b", "c"]
 => ["a", "b", "c"]
2.7.3 :023 > x.pop
 => "c"
2.7.3 :024 > x
 => ["a", "b"]
2.7.3 :025 > x.pop
 => "b"
2.7.3 :026 > x
 => ["a"]
2.7.3 :027 > x.pop
 => "a"
def reverse_vowels(s)
  vowels = s.scan(/[aeiou]/i)
  s.gsub(/[aeiou]/i) { vowels.pop }
end

完成!


<<:  [从0到1] C#小乳牛 练成基础程序逻辑 Day 3 - Hello World

>>:  Day6一个网站总需要一点首页吧!

【第十天 - Two-pointer 介绍】

Q1. Two-pointer 是什麽? 我个人认为双指标 ( Two-pointer ) 比较像写...

[Day 15] Reverse 小忙碌

今天我过得很充实, 前几天有看到读者的留言 很感激你们愿意花时间看我的文章 看我分享我每日小生活 为...

[Python 爬虫这样学,一定是大拇指拉!] DAY19 - Python:Requests 基本应用 (2)

今天要来讲的是,读取送出 Request 後拿回来的 Response。 读取 Response 以...

Day 15 conda 介绍与使用

当我们在使用 Python 开发专案时,不同专案可能会有不同 Python 版本和不同的 packa...

[Day 18] Migration

当多人一起开发专案时,Migration可以让团队修改、设定资料库的内容,像是资料库的版本控制,会纪...