Ruby正则表达式

03 Aug 2013, by

Ruby正则表达式编辑器:Rubular

Ruby的正则表达式以/pattern/为模式,表达式返回一个RegExp的对象。可以通过内省方法来查看:

puts /RoR|Rails/.class #-> RegExp

Ruby中正则表达式的写法

主要有三种

在//之间,要进行转义
在%r{}内,不用进行转义
Regexp.new()内,不用进行转义
/mm\/dd/,Regexp.new(“mm/dd”),%r{mm/dd}三者效果相同,实质都是新建了一个Regexp的类。

匹配的两种方法

=~肯定匹配, !~否定匹配。=~表达式返回匹配到的位置索引,失败返回nil,符号左右内容可交换 regexp#match(str),返回MatchData,一个数组,从0开始,还有 match.pre_match 返回匹配前内容,match.post_match返回匹配后内容

/cat/ =~ "dog and cat" 	#返回8   
mt = /cat/.match("bigcatcomes")   
"#{mt.pre_match}->#{mt[0]}<-#{mt.post_match}" #返回big->cat<-comes   

替换

很多时候匹配是为了替换,Ruby中进行正则替换非常简单,两个方法即可搞定,sub()+gsub()。 sub只替换第一次匹配,gsub(g:global)会替换所有的匹配,没有匹配到返回原字符串的copy

str = "ABDADA"
new_str = str.sub(/A/, "*") 	#返回"*BDADA"
new_str2 = str.gsub(/A/, "*")	#返回"*BD*D*"

如果想修改原始字符串用sub!()和gsub!(),没有匹配到返回nil。

方法后面还可以跟block,对匹配的字符串进行操作

a.gsub(/[aeiou]/) {|vowel| vowel.upcase } # => "qUIck brOwn fOx"

分组匹配

Ruby的分组匹配与其它语言差别不大,分组匹配表达式是对要进行分组的内容加()。 对于匹配到的结果,可以用系统变量#$1,#$2…索引,也可用matchData数组来索引

md = /(\d\d):(\d\d)(..)/.match("12:50am") # md为一个MatchData对象
puts "Hour is #$1, minute #$2"
puts "Hour is #{md[1]}, minute #{md[2]}"

匹配所有

regexp#match()只能匹配一次,如果想匹配所有要用regexp#scan()
用法示例:

"abcabcabz".scan(%r{abc}).each {|item| puts item} # 输出2行abc

贪婪匹配vs懒惰匹配

这两种匹配属于标准正则表达式内容,与Ruby没关,但新手如果不明白匹配时会发生莫名其妙的错误,所以特别总结一下。

贪婪匹配:尽可能多匹配,正则默认是贪婪匹配。例子:a.*b它将会匹配最长的以a开始,以b结束的字符串。对于aabab的匹配结果是aabab。
懒惰匹配:尽可能少匹配。例子:a.*?b对于aabab的匹配结果是aab和ab。 一般是在原来表达式结尾加?就由贪婪匹配变成了懒惰匹配。常用的懒惰限定符有(去年最后的问题就是贪婪匹配):

?重复任意次,但尽可能少重复
+?重复1次或更多次,但尽可能少重复
??重复0次或1次,但尽可能少重复
{n,m}?重复n到m次,但尽可能少重复
{n,}?重复n次以上,但尽可能少重复

一般规则(为正常显示,都放在代码块内)

/a/匹配字符a。    
/\?/匹配特殊字符?。特殊字符包括^, $, ? , ., /, \, [, ], {, }, (, ), +, *.    
.匹配任意字符,例如/a./匹配ab和ac。    
/[ab]c/匹配ac和bc,[]之间代表范围。例如:/[a-z]/ , /[a-zA-Z0-9]/。    
/[^a-zA-Z0-9]/匹配不在该范围内的字符串。    
/[\d]/代表任意数字
/[\w]/代表任意字母,数字或者_
/[\s]/代表空白字符,包括空格,TAB和换行。    
/[\D]/,/[\W]/,/[\S]/均为上述的否定情况。 

高级规则

?代表0或1个字符。/Mrs?\.?/匹配"Mr","Mrs","Mr.","Mrs."。 
*代表0或多个字符。/Hello*/匹配"Hello","HelloJack"。 
+代表1或多个字符。/a+c/匹配:"abc","abbdrec"等等。 
/d{3}/匹配3个数字。 
/d{1,10}/匹配1-10个数字。 
/d{3,}/匹配3个数字以上。 
/([A-Z]\d){5}/匹配首位是大写字母,后面4个是数字的字符串。

正则表达式操作

String和RegExp均支持=~和match两个查询匹配方法:

puts "I can say my name" =~ /name/ #-> 13

a = /name/.match("I can say my name, my name I can say") #-> a is MatchData
puts a[0] #-> name

可以看出,如果能够匹配,=~返回匹配的字符串位置,而match返回一个MatchData对象。如果不匹配,则返回nil。MatchData可以取出其中符合各个子匹配(或子模式)的内容,看下面的例子:

b1=/[A-Za-z]+,[A-Za-z]+,Mrs?\./.match("Jack,Wang,Mrs., nice person")
puts b1[0] #-> Jack,Wang,Mrs

b2=/(([A-Za-z]+),([A-Za-z]+)),Mrs?\./.match("Jack,Wang,Mrs., nice person:)
puts b2[0] #-> Jack,Wang,Mrs
puts b2[1] #-> Jack,Wang
puts b2[2] #-> Jack
puts b2[3] #-> Wang

m[0]返回匹配匹配主表达式的字符串,下面的方法是等同的:m[n]==m.captures[n]

Ruby也自动的为我们填写一些全局变量,它们以数字做名,$1, $2, 等等,$1包含的是正则表达式中从左侧开始的第一对小括号内的子模式所匹配的字符串,以此类推。我们看出匹配时,是从外到内,从左到右的顺序。

贪婪量词和不贪婪量词

量词*(表示零个或多个)和+(表示一个或多个)是贪婪的,它们会匹配尽可能多的字符,我们可以在*和+后面加一个?,使它成为非贪婪量词。

下面代码是: 1个或多个字符后接一个感叹号。

teststr="abcd!efg!"
match=/.+!/.match(teststr)
puts match[0] #-> abcd!efg!

limitmatch=/.+?!/.match(teststr)
puts limitmatch[0] #-> abcd!

锚是指必须满座一定的条件,才能继续匹配:

^     行首
$     行尾
\A    字符串的开始
\z    字符串的结尾
\Z    字符串的结尾(不包括最后的换行符)
\b    单词边界

c=/\b\w+\b/.match("!!Stephen**")
puts c[0] #-> Stephen

前视断言

前视断言表示想要知道下一个指定的是什么,但并不匹配

肯定的前视断言 (?=)   

假设我们想要匹配一个数的序列,该序列以一个圆点结束,但并不想把圆点作为模式匹配的一部分

teststr="123 456 789. 012"
m=/\d+(?=\.)/.match(teststr)
puts m[0] #-> 789
否定的前视断言 (?!)   

上例,如果/\d+(?=.)/改为/\d+(?!.)/,则puts m[0]输出显示为 123 。

修饰语

修饰语位于正则表达式最结束正则表达式的正斜杠的后面

i使正则表达式对大小写不敏感   

例如,/abc/i可以匹配Abc,abc,ABC等。

m使得正则表达式可以和任何字符匹配,包括换行符,通常情况下圆点通配符不匹配换行符。   

字符串与正则表达式的相互转换

字符串内插进正则表达式   
teststr="a.c"
re=/#{Regexp.escape(teststr)}/
puts re.match("a.c")[0] #-> a.c
test=re.match("abc")
puts test[0] #-> Nil
正则表达式转换成字符串

puts /abc/.inspect #-> /abc/

使用正则表达式的常见方法

    用于if和while等

    用于gsub、grep等

    用于find_all、scan等

例如,puts “test 1 2 and test 3 4”.scan(/\d/)会输出[”1”,”2”,”3”,”4”]。