ruby way之正则表达式之一

本文详细介绍Ruby中正则表达式的使用方法,包括编译、转义、锚定、量词及高级用法等,帮助读者掌握Ruby正则表达式的各种技巧。
在ruby中内置了正则表达式,如果你的ruby版本是1.9或者更后面的版本的话,你的正则表达式引擎将会是一个叫做Oniguruma的东西。这是一个新的引擎,代替了1.8版本中的这则表达式引擎,我们后面会介绍到它。

1编译正则表达式:
正则表达式能够使用Regexp.compile 方法进行编译(这个方法同Regexp.new方法是同义的),
参数可以是一个字符串或者一个正则式(这边要注意一个问题,如果参数是正则式的话,他所附带的参数将会被传播,你可以不用指定后续的第二个参数,这个马上就会看到),看下面的例子:
[code]
test1=Regexp.compile("Bar");
test=Regexp.compile(/Bar/i); #忽略大小写
puts test1.match("bar") #nil
puts test.match("bar") #bar[/code]
第二个参数,如果存在的话可以是由||连接起来的 下面几个值Regexp::EXTENDED, Regexp::IGNORECASE, Regexp::MULTILINE。第二个参数如果是true代表大小写不敏感,否则代表大小写敏感。可是如果参数是一个正则式的话,第二个参数将会被忽略.

[code]options = Regexp::MULTILINE || Regexp::IGNORECASE
pat3 = Regexp.compile("^foo", true)
pat4 = Regexp.compile(/Bar/, Regexp::IGNORECASE)

puts pat3.match("Doo") #这边大小写不敏感,所以返回doo
puts pat4.match("bar") #这边将会返回nil,因为第二个参数被忽略了,ruby解释器会给你提醒的..[/code]

第三个参数启用对正则表达式的多字节支持。同样的如果使用正则式,它将会被忽略.
[code]"N" or "n" means None
"E" or "e" means EUC
"S" or "s" means Shift-JIS
"U" or "u" means UTF-8[/code]

最后,我们还可以直接使用//来定义正则表达式,而不需要任何compile方法。。

[code]pat1 = /^foo.*/
pat2 = /bar$/i[/code]

2 转义特殊字符

我们可以使用Regexp.escape 方法来转义特殊字符,转义的返回值就是所能匹配的正则表达式,也就是说 Regexp.new(Regexp.escape(str)) =~str一定返回true.

[code]str1 = "[*?]"
str2 = Regexp.escape(str1) # "\[\*\?\]"
puts str1 =~Regexp.new(str2) #返回0[/code]

3使用锚

一个锚是一个特殊的匹配一个字符串中的位置的表达式。

最简单的锚就是 ^和$,它们分别匹配一个字符串的开始或者结尾的位置.

[code]strings = "abcXdefXghi"
puts strings =~/def/ #4
puts strings =~/abc/ # 0
puts strings =~/ghi/ # 8
puts strings =~/^def/ # nil
puts strings =~/def$/ # nil
puts strings =~/^abc/ # 0
puts strings =~/ghi$/ # 8[/code]

可是这边小小的撒了个谎,其实^和$并不是真正的匹配字符串的开始或结尾,它们会分隔多行,也就是说如果有多行的话,他们会匹配多个开始和结尾,我们看下面的例子:

[code]strings = "abc\ndef\nghi" #这边我们加入了几个回车


puts strings =~/def/ #4
puts strings =~/abc/ # 0
puts strings =~/ghi/ # 8
puts strings =~/^def/ # 4 #它会把新的一行的第一个字符匹配
puts strings =~/def$/ # 4
puts strings =~/^abc/ # 0
puts strings =~/ghi$/ # 8[/code]

可是我们这边有两个真正的匹配字符串的开始或者结尾的锚,他们是 \A 和 \Z :
[code]string = "abc\ndef\nghi"
puts string =~/\Adef/ # nil
puts string =~/def\Z/ # nil
puts string =~/\Aabc/ # 0
puts string =~/ghi\Z/ # 8[/code]

这边还有一个\z(小z),它和\Z不同的是,它不会匹配最后一个回车。:

[code]string = "abc\ndef\nghi"
puts string =~ /ghi\Z/ # 8
puts string =~/\Aabc/ # 0
puts string =~/ghi\Z/ # 8
puts string =~/ghi\z/ # 8
string << "\n"
puts string =~/ghi\z/ # nil #由于有最后一个回车,因此这边不能匹配[/code]

还可以用\b来匹配单词的边界,或者\B匹配一个不是单词边界的位置..

[code]str = "this is a test"
puts str.gsub(/\b/,"|") # "|this| |is| |a| |test|"
puts str.gsub(/\B/,"-") # "t-h-i-s i-s a t-e-s-t"[/code]

4 使用量词符号

正则表达式中很大的一部分就是表示可选的字符和字符串重复.一个字符后面跟着一个问号,代表这个字符可以是1个或者0个:
[code]pattern = /ax?b/
pat2 = /a[xy]?b/
puts "ab" =~pattern # 0
puts "acb" =~pattern # nil
puts "axb" =~ pattern # 0
puts "ayb" =~pat2 # 0
puts "acb" =~pat2 # nil[/code]

我们可以使用+来匹配一个或者多个字符:
[code]pattern = /[0-9]+/
puts pattern =~ "1" # 0
puts pattern =~ "2345678" # 0[/code]

另外一个经常使用的匹配模式是,匹配0个或者多个,我们可以使用+和?的组合来实现:

[code]pattern = /Huzzah(!+)?/ # Parentheses are necessary here
puts pattern =~ "Huzzah" # 0
puts pattern =~ "Huzzah!!!!" # 0[/code]

但是这里有一个更好的办法,那就是*号:

[code]pattern = /Huzzah!*/ # * applies only to !
puts pattern =~ "Huzzah" # 0
puts pattern =~ "Huzzah!!!!" # 0[/code]

如果我们相匹配一个U.S. Social Security Number,我可以这样做:

[code]ssn = "987-65-4320"
pattern = /\d\d\d-\d\d-\d\d\d\d/
puts pattern =~ ssn # 0

#看起来很丑陋,我们其实可以使用更好的方法,那就是使用{}符号,它里面放一个数字#n,代表着一个字符重复n次:

pattern = /\d{3}-\d{2}-\d{4}/
puts pattern =~ ssn # 0[/code]

逗号分隔符还可以被放进{},比如{start,end},其中start指的是最少的重复次数,end指的是最大的重复字数,其中start和end都是可选的,可是两个必须存在一个:
[quote]/x{5}/ # 匹配 5 个x
/x{5,7}/ # 匹配 5到7 个x
/x{,8}/ #匹配 最多8 个x
/x{3,}/ #匹配最少 3个x[/quote]

正则表达式的专用术语都是充满了人性化,比如greedy, reluctant, lazy, and possessive.

思索下面的代码,一个字符串
[code]str = "Where the sea meets the moon-blanch'd land,"[/code] 我们只想匹配
"Where the", 可是经常会匹配到一个更大的子字符串"Where the sea meets the" 。:

[code]str = "Where the sea meets the moon-blanch'd land,"
match = /.*the/.match(str)
puts match[0] # here the sea meets the[/code]

为什么会这样子呢?这是由于*是一个greedy匹配,也就是一个贪婪匹配,他总是匹配,他所能匹配的最长的字符串,如果我们要匹配"Where the",则我们可以这么做:

[code]str = "Where the sea meets the moon-blanch'd land,"
match = /.*?the/.match(str)
puts match[0] # "Where the" [/code]

这边给* 后面加一个?就会生成一个非贪婪的匹配,这条规则对+和{m,n} 都是一样成立的,甚至对?他自己。

这边作者举不出{m,n}?和??的例子,说谁有好的例子可以告诉他..

5 Positive and Negative Lookahead(这个我也不知道怎么翻译...)

这个我觉得 Mastering.Regular.Expressions,讲的更好,我就按自己理解的说了:

这边有两个符号,一个是Positive lookahead也就是(?= ),还有一个Negative lookahead
也就是(?! )。这两个符号其实很容易理解,两个匹配的都是一个分界,而不是其他的,其中(?= )匹配的是括号里面字符的前面所匹配的那个点,比如 s="abc" (?=bc)匹配的就是a和bc之间的那个点. 而(! )恰恰相反,也就是非那个点。。

[code]reg = /New World(?= Dictionary| Symphony)/
m1 = reg.match(s1)
puts m1.to_a[0] # "New World"
m2 = reg.match(s2)
puts m2.to_a[0] # "New World"
m3 = reg.match(s3)
puts m3.to_a[0] # nil


reg2 = /New World(?! Symphony)/
m1 = reg2.match(s1)
puts m1.to_a[0] # "New World"
m2 = reg2.match(s2)
puts m2.to_a[0] # nil
m3 = reg2.match(s3)
puts m3.to_a[0] # "New World"[/code]

6 访问后引用

每一个小括号里面的正则表达式都会有它自己的一个子匹配,他们能通过一些方法来得到这些子匹配,比如全局的变量 $1,$2等等。。:
[code]str = "a123b45c678"
if /(a\d+)(b\d+)(c\d+)/ =~ str
puts "Matches are: '#$1', '#$2', '#$3'"
# 打印出: Matches are: 'a123', 'b45', 'c768'
end[/code]

作者这里说象在sub,gsub方法里面$1之类的不能用,我的ruby是1.9 ,sub和gsub是可以用的:

[code]str = "a123b45c678"
puts str.sub(/(a\d+)(b\d+)(c\d+)/, "1st=#$1, 2nd=#$2, 3rd=#$3")
#打印出1st=a123, 2nd=b45, 3rd=c678[/code]
在sub和gsub方法中我们还可以使用\1 ..来替代$1:

[code]str = "a123b45c678"
puts str.sub(/(a\d+)(b\d+)(c\d+)/, '1st=\1, 2nd=\2, 3rd=\3')
# "1st=a123, 2nd=b45, 3rd=c678"[/code]

注意我们这里使用的是单引号,这是因为双引号会把\1转义,如果要在双引号中使用\1,我们必须用\\1来替代:

[code]str = "a123b45c678"
puts str.sub(/(a\d+)(?:b\d+)(c\d+)/, "1st=\\1, 2nd=\\2, 3rd=\\3")
# "1st=a123, 2nd=c678, 3rd="[/code]

在上面的例子中第二个匹配打印出来的是第三个,而第三个匹配,却是nil,注意,这边第二个括号,我们使用的是(?: )这个符号,这个会不捕捉他所匹配的子字符串,所以这里b45没有被打印出来.

$1这种标记虽然有时候看起来很好用,可是我们可以使用更面向对象一点的方,Regexp.match方法这个方法返回一个MatchData类,它包含所有的子匹配,我们可以把它转换成一个数组来取得所有的子匹配:

[code]pat = /(.+[aiu])(.+[aiu])(.+[aiu])(.+[aiu])/i
# Four identical groups in this pattern
refs = pat.match("Fujiyama")
puts refs.to_a.each {|xx| print "#{xx}\n"}
# 打印出: ["Fujiyama","Fu","ji","ya","ma"][/code]

我们还可以使用begin和end方法,来返回匹配的位移:

[code]str = "alpha beta gamma delta epsilon"
# 0....5....0....5....0....5....
# (for your counting convenience)

pat = /(b[^ ]+ )(g[^ ]+ )(d[^ ]+ )/
# Three words, each one a single match
refs = pat.match(str)

# "beta "
puts p1 = refs.begin(1) # 6 返回所经过的位移
puts p2 = refs.end(1) # 11
# "gamma "
puts p3 = refs.begin(2) # 11
puts p4 = refs.end(2) # 17
# "delta "
puts p5 = refs.begin(3) # 17
puts p6 = refs.end(3) # 23
# "beta gamma delta"
puts p7 = refs.begin(0) # 6
puts p8 = refs.end(0) # 23[/code]

而offset方法返回一个2个元素的数组他分别是开始和结束的偏移量:

[code]puts range0 = refs.offset(0) # [6,23]
puts range1 = refs.offset(1) # [6,11]
puts range2 = refs.offset(2) # [11,17]
puts range3 = refs.offset(3) # [17,23][/code]

pre_match 和post_match方法返回当前匹配的前一个前面部分和后面部分.

[code]puts before = refs.pre_match # "alpha "
puts after = refs.post_match # "epsilon"[/code]

刚好去除掉匹配的东西,留下的就是我们打印出来的..
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值