Ruby 正则表达式学习笔记

Ruby 正则表达式的字面构造器:

//
试一下:

>> //.class
=> Regexp
模式匹配有两个部分组成,一个正则表达式(regexp),还有一个字符串。正则表达式预测字符串,字符串要么满足预测,要么不满足。

看看是不是匹配可以使用 match 方法。做个实验:

>> puts "匹配!" if /abc/.match("the alphabet starts with abc.")
匹配!
=> nil
>> puts "匹配!" if "the alphabet starts with abc.".match(/abc/)
匹配!
=> nil
除了 match 方法,还有个模式匹配操作符 =~ ,把它放在字符串与正则表达式的中间用:

>> puts "匹配!" if /abc/ =~ "the alphabet starts with abc."
匹配!
=> nil
>> puts "匹配!" if "the alphabet starts with abc." =~ /abc/
匹配!
=> nil
没有匹配就会返回 nil 。有匹配的话,match 与 =~ 返回的东西不一样。 =~ 返回是匹配开始的字符的数字索引,match 返回的是 MatchData 类的一个实例。做个实验:

>> "the alphabet starts with abc" =~ /abc/
=> 25
>> /abc/.match("the alphabet starts with abc")
=> #<MatchData "abc">
匹配模式

// 中间的东西可不是字符串,它是你对字符串做的预测与限制。

字面字符

正则表达式里的字面字符匹配它自己,比如:

/a/
匹配的就是字母 a 。

有些字符有特别的意思,如果你不想让它表达特别的意思,可以使用 \ 线 escape 一下它:

/\?/
通配符

. 表示除了换行符以外的任意字符。

/h.t/
匹配 hot,hit ...

字符类

字符类会在一组方括号里:

/h[oi]t/
在上面的正则表达式里,那个字符类的意思是匹配 o 或 i 。也就是上面这个模式会匹配 “hot”,“hit”,但不匹配 “h!t” 或其它的东西。

下面这个字符类匹配小写字符 a 到 z :

/[a-z]/
^ 符号在字符类里表示否定:

/[^a-z]/
匹配十六进制数字,在字符类里可能得用几个字符范围:

/[A-Fa-f0-9]/
匹配数字 0 - 9:

/[0-9]/
0 - 9 太常用了,所以还有个简单的形式,d 表示 digit:

/\d/
数字,字符,还有下划线,w 表示 word:

/\w/
空白,比如空格,tab,还有换行符,s 表示 space:

/\s/
大写的表示否定形式:\D,\W,\S。

匹配

给你 yes/no 结果的匹配的操作:

regex.match(string)
string.match(regex)
子匹配

比如我们有行文字是关于一个人的:

Peel,Emma,Mrs.,talented amateur
我想得到人的 last name,还有 title。我们知道字段是用逗号分隔开的,我们也知道顺序:last name,first name,title,occupation 。

首先是一些字母字符,
然后是一个逗号,
然后又是一些字母字符,
接着还是一个逗号
然后是 Mr. 或 Mrs.
匹配上面这种字符器的模式:

/[A-Za-z]+,[A-Za-z]+,Mrs?\./
s? 意思是这个 s 可以有也可以没有。这样 Mrs? 也就会匹配 Mr 或 Mrs 。在 irb 上做个实验:

>> /[A-Za-z]+,[A-Za-z]+,Mrs?\./.match("Peel,Emma,Mrs.,talented amateur")
=> #<MatchData "Peel,Emma,Mrs.">
我们得到了一个 MatchData 对象。现在我们要 Pell 还有 Mrs 怎么办? 可以使用括号对匹配模式分组:

/([A-Za-z]+),[A-Za-z]+,(Mrs?\.)/
再试试这个匹配模式:

>> /([A-Za-z]+),[A-Za-z]+,(Mrs?\.)/.match("Peel,Emma,Mrs.,talented amateur")
=> #<MatchData "Peel,Emma,Mrs." 1:"Peel" 2:"Mrs.">
使用 $1 可以得到第一个分组里的匹配,$2 可以得到第二个分组里的匹配:

>> puts $1
Peel
=> nil
>> puts $2
Mrs.
=> nil
匹配成功与失败

没找到匹配,返回的值就是 nil,试试:

>> /a/.match("b")
=> nil
如果匹配成功会返回 MatchData 对象,它的布尔值是 true。还有些关于匹配的信息,比如匹配在哪里开始,覆盖了多少字符串,在分组里获得了什么等等。

想使用 MatchData 得先把它存储起来。练习一下:

string = "我的电话号码是 (123) 555-1234."
phone_re = /\((\d{3})\)\s+(\d{3})-(\d{4})/
m = phone_re.match(string)

unless m
  puts "没有匹配"
  exit
end

print "整个字符串:"
puts m.string
print "匹配:"
puts m[0]
puts "三个分组:"
3.times do |index|
  puts "#{index + 1}:#{m.captures[index]}"
end
puts "得到第一个分组匹配的内容:"
puts m[1]
结果是:

整个字符串:我的电话号码是 (123) 555-1234.
匹配:(123) 555-1234
三个分组:
1:123
2:555
3:1234
得到第一个分组匹配的内容:
123
得到捕获的两种方法

从 MatchData 对象里得到匹配模式分组捕获到的内容:

m[1]
m[2]
...
m[0] 得到的是匹配的全部内容。

另一种得到分组捕获内容的方法是使用 captures 方法,它返回的是一个数组,数组里的项目就是捕获的子字符串。

m[1] == m.captures[0]
m[2] == m.captures[1]
再看个例子:

>> /((a)((b)c))/.match("abc")
=> #<MatchData "abc" 1:"abc" 2:"a" 3:"bc" 4:"b">
命名捕获

>> re = /(?<first>\w+)\s+((?<middle>\w\.)\s+)?(?<last>\w+)/
匹配:

>> m = re.match("Samuel L. Jackson")
=> #<MatchData "Samuel L. Jackson" first:"Samuel" middle:"L." last:"Jackson">
>> m[:first]
=> "Samuel"
>> m[:last]
=> "Jackson"
MatchData 的其它信息

接着之前的电话号码的例子:

print "匹配之前的部分:"
puts m.pre_match

print "匹配之后的部分:"
puts m.post_match

print "第二个捕获开始字符:"
puts m.begin(2)

print "第三个捕获结束字符:"
puts m.end(3)
输出的结果是:

匹配之前的部分:我的电话号码是
匹配之后的部分:.
第二个捕获开始字符:14
第三个捕获结束字符:22
begin 与 end 方法待验证。
Quantifiers,Anchors,Modifiers

Quantifiers(限定符),Anchors(标记),Modifiers(修饰符)。

限定符

限定符可以指定在匹配里某个东西要匹配的次数。

零或一

?
例:

/Mrs?/
s 可以出现零次或一次。

零或多

*
例:

/\d*/
一或多

+
例:

/\d+/
Greedy quantifier

*与+ 这两个限定符都很 greedy。意思就是它们会尽可能的匹配更多的字符。

观察下面这个例子里的 .+ 匹配的是什么:

>> string = "abc!def!ghi!"
=> "abc!def!ghi!"
>> match = /.+!/.match(string)
=> #<MatchData "abc!def!ghi!">
>> puts match[0]
abc!def!ghi!
=> nil
你可能期望返回的是子字符 "abc!" ,不过我们得到的是 "abc!def!ghi!"。限定符 + 贪婪的吃掉了它能覆盖的所有的字符,一直到最后一个 ! 号结束。

我们可以在 + 与 * 后面添加一个 ? 号,让它们不那么贪婪。再试一下:

>> string = "abc!def!ghi!"
=> "abc!def!ghi!"
>> match = /.+?!/.match(string)
=> #<MatchData "abc!">
>> puts match[0]
abc!
=> nil
再做个实验:

>> /(\d+?)/.match("Digits-R-Us 2345")
=> #<MatchData "2" 1:"2">
>> puts $1
2
=> nil
再看个匹配:

>> /\d+5/.match("Digits-R-Us 2345")
=> #<MatchData "2345">
这样再试一下:

>> /(\d+)(5)/.match("Digits-R-Us 2345")
=> #<MatchData "2345" 1:"234" 2:"5">
具体重复的次数

把次数放到 {} 里。下面匹配的是三个数字,小横线,接着是四个数字:

/\d{3}-\d{4}/
也可能是一个范围,下面匹配的是 1 到 10 个数字:

/\d{1,10}/
大括号里第一个数字是最小值,下面匹配的是 3 个或更多的数字:

/\d{3,}/
括号的限制

>> /([A-Z]){5}/.match("Matt DAMON")
=> #<MatchData "DAM" 1:"N">
你期望的匹配可能是 DAMON,但实际匹配的是 N 。如果你想匹配 DAMON ,需要这样做:

>> /([A-Z]{5})/.match("Matt DAMON")
=> #<MatchData "DAMON" 1:"DAMON">
anchors 与 assertions

anchors(标记,锚) 与 assertions(断言):在处理字符匹配之前先要满足一些条件。

^ 表示行的开始,$ 行的结尾。

Ruby 里的注释是 # 号开头的,匹配它的模式可以像这样:

/^\s*#/
^ 匹配的是行的最开始。

anchors

^:行的开始
$:行的结尾
\A:字符串的开始
\z:字符串的结尾
\Z:字符串的结尾,模式:/from the earth.\Z/,匹配:"from the earth\n"
\b:字边界
lookahead assertions

你想匹配一组数字,它的结尾必须有点,但你不想在匹配的内容里包含这个点,可以这样做:

>> str = "123 456. 789"
=> "123 456. 789"
>> m = /\d+(?=\.)/.match(str)
=> #<MatchData "456">
lookbehind assertions

我要匹配 Damon,但必须它的前面得有 matt。

模式:

/(?<=Matt )Damon/
试一下:

>> /(?<=Matt )Damon/.match("Matt Damon")
=> #<MatchData "Damon">
>> /(?<=Matt )Damon/.match("Matt1 Damon")
=> nil
我要匹配 Damon,但它的前面不能是 matt 。

模式:

/(?<!Matt )Damon/
试一下:

>> /(?<!Matt )Damon/.match("Matt Damon")
=> nil
>> /(?<!Matt )Damon/.match("Matt1 Damon")
=> #<MatchData "Damon">
不捕获

使用:?:

>> str = "abc def ghi"
=> "abc def ghi"
>> m = /(abc) (?:def) (ghi)/.match(str)
=> #<MatchData "abc def ghi" 1:"abc" 2:"ghi">
条件匹配

条件表达式:(?(1)b|c) ,如果获取到了 $1,就匹配 b ,不然就匹配的是 c :

>> re = /(a)?(?(1)b|c)/
=> /(a)?(?(1)b|c)/
>> re.match("ab")
=> #<MatchData "ab" 1:"a">
>> re.match("b")
=> nil
>> re.match("c")
=> #<MatchData "c" 1:nil>
有名字的:

/(?<first>a)?(?(<first>)b|c)/
modifiers

i 这个修饰符表示不区分大小写:

/abc/i
m 表示多行:

/abc/m
x 可以改变正则表达式解析器对待空格的看法,它会忽略掉在正则表达式里的空格,除了你用 \ 符号 escape 的空白。

/
 \((\d{3})\)  # 3 digits inside literal parens (area code)
   \s         # One space character
 (\d{3})      # 3 digits (exchange)
    -         # Hyphen
 (\d{4})      # 4 digits (second part of number
/x
转换字符串与正则表达式

string-to-regexp

在正则表达式里使用插值:

>> str = "def"
=> "def"
>> /abc#{str}/
=> /abcdef/
如果字符串里包含在正则表达式里有特别意义的字符,比如点(.):

>> str = "a.c"
=> "a.c"
>> re = /#{str}/
=> /a.c/
>> re.match("a.c")
=> #<MatchData "a.c">
>> re.match("abc")
=> #<MatchData "abc">
你可以 escape 这些特殊的字符:

>> Regexp.escape("a.c")
=> "a\\.c"
>> Regexp.escape("^abc")
=> "\\^abc"
这样再试试:

>> str = "a.c"
=> "a.c"
>> re = /#{Regexp.escape(str)}/
=> /a\.c/
>> re.match("a.c")
=> #<MatchData "a.c">
>> re.match("abc")
=> nil
也可以:

>> Regexp.new('(.*)\s+Black')
=> /(.*)\s+Black/
这样也行:

>> Regexp.new('Mr\. David Black')
=> /Mr\. David Black/
>> Regexp.new(Regexp.escape("Mr. David Black"))
=> /Mr\.\ David\ Black/
regexp-to-string

正则表达式可以使用字符串的形式表示它自己:

>> puts /abc/
(?-mix:abc)
inspect:

>> /abc/.inspect
=> "/abc/"
使用正则表达式的方法

Ruby 里的一些方法可以使用正则表达式作为它们的参数。

比如在一个数组里,你想找出字符长度大于 10 ,并且包含一个数字的项目:

array.find_all {|e| e.size > 10 and /\d/.match(e) }
String#scan

找到一个字符串里包含的所有的数字:

>> "testing 1 2 3 testing 4 5 6".scan(/\d/)
=> ["1", "2", "3", "4", "5", "6"]
分组:

>> str = "Leopold Auer was the teacher of Jascha Heifetz."
=> "Leopold Auer was the teacher of Jascha Heifetz."
>> violinists = str.scan(/([A-Z]\w+)\s+([A-Z]\w+)/)
=> [["Leopold", "Auer"], ["Jascha", "Heifetz"]]
可以这样用:

violinists.each do |fname,lname|
  puts "#{lname}'s first name was #{fname}."
end
输出的是:

Auer's first name was Leopold.
Heifetz's first name was Jascha.
合并到一块儿:

str.scan(/([A-Z]\w+)\s+([A-Z]\w+)/) do |fname, lname|
  puts "#{lname}'s first name was #{fname}."
end
再做个实验:

"one two three".scan(/\w+/) {|n| puts "Next number: #{n}" }
输出的是:

Next number: one
Next number: two
Next number: three
如果你提供了一个代码块,scan 不会存储把结果存储到一个数组里,它会把每个结果都发送给代码块,然后扔掉结果。也就是你可以 scan 一个很长的东西,不用太担心内存的问题。

StringScanner

StringScanner 在 strscan 扩展里,它里面提供了一些扫描与检查字符串的工具。可以使用位置与指针移动。

>> require 'strscan'
=> true
>> ss = StringScanner.new("Testing string scanning")
=> #<StringScanner 0/23 @ "Testi...">
>> ss.scan_until(/ing/)
=> "Testing"
>> ss.pos
=> 7
>> ss.peek(7)
=> " string"
>> ss.unscan
=> #<StringScanner 0/23 @ "Testi...">
>> ss.pos
=> 0
>> ss.skip(/Test/)
=> 4
>> ss.rest
=> "ing string scanning"
String#split

split 可以把一个字符串分离成多个子字符串,返回的这些子字符串会在一个数组里。split 可以使用正则表达式或者纯文字作为分隔符。

试一下:

>> "Ruby".split(//)
=> ["R", "u", "b", "y"]
把一个基于文字的配置文件的内容转换成 Ruby 的数据结构。

>> line = "first_name=matt;last_name=damon;country=usa"
=> "first_name=matt;last_name=damon;country=usa"
>> record = line.split(/=|;/)
=> ["first_name", "matt", "last_name", "damon", "country", "usa"]
hash:

>> data = []
=> []
>> record = Hash[*line.split(/=|;/)]
=> {"first_name"=>"matt", "last_name"=>"damon", "country"=>"usa"}
>> data.push(record)
=> [{"first_name"=>"matt", "last_name"=>"damon", "country"=>"usa"}]
split 的第二个参数,可以设置返回的项目数:

>> "a,b,c,d,e".split(/,/,3)
=> ["a", "b", "c,d,e"]
sub/sub! 与 gsub/gsub!

sub 与 gsub,可以修改字符串里的内容。gsub 修改整个字符串,sub 最多只修改一个地方。

sub

>> "hit hit".sub(/i/,"o")
=> "hot hit"
代码块:

>> "hit".sub(/i/) {|s| s.upcase}
=> "hIt"
gsub

>> "hit hit".gsub(/i/,"o")
=> "hot hot"
捕获

>> "oHt".sub(/([a-z])([A-Z])/, '\2\1')
=> "Hot"
>> "double every word".gsub(/\b(\w+)/, '\1 \1')
=> "double double every every word word"
=== 与 grep

===

所有的 Ruby 对象都认识 === 这个信息,如果你没覆盖它的话,它跟 == 是一样的。如果你覆盖了 === ,那它的功能就是新的意思了。

在正则表达式里,=== 的意思是匹配的测试。

puts "Match!" if re.match(string)
puts "Match!" if string =~ re
puts "Match!" if re === string
试一下:

print "Continue? (y/n) "
answer = gets
case answer
when /^y/i
  puts "Great!"
when /^n/i
  puts "Bye!"
  exit
else
  puts "Huh?"
end
grep

>> ["USA", "UK", "France", "Germany"].grep(/[a-z]/)
=> ["France", "Germany"]
select 也可以:

["USA", "UK", "France", "Germany"].select {|c| /[a-z]/ === c }
代码块:

>> ["USA", "UK", "France", "Germany"].grep(/[a-z]/) {|c| c.upcase }
=> ["FRANCE", "GERMANY"]

 

时间: 2024-08-01 21:49:29

Ruby 正则表达式学习笔记的相关文章

js正则表达式学习笔记_正则表达式

正则表达式:对字符串中的信息实现查找.替换和提取操作.(不支持注释和空白,必须写在一行内)正则表达式的创建:包含在一对斜杠之间的字符(直接量语法) 例如: var pattern = /s$/; // 创建一个正则来匹配所有以字母s结尾的字符串,并赋值给pattern 一 .字符类将直接量字符单独放进方括号内就组成了字符类. 一个字符类可以匹配它所包含的任意字符.正则表达式的字符类:[...]      方括号内的任意字符[^...]    不在方括号内的任意字符.           除换行符

php正则表达式学习笔记_php技巧

php正则表达式学习笔记分享: 1.创建正则表达式 $regex = '/\d/i'; 与JavaScript中的第一个方式有点像,只是这里的话是个字符串.  2.正则表达式中的特殊字符 特殊字符有:   . \ + * ? [ ^ ] $ ( ) { } = ! < > | : - 3.正则表达式中的函数 有8个方法,preg_match与preg_match_all,preg_replace与preg_replace_callback,preg_grep.preg_split.preg_l

正则表达式学习笔记_正则表达式

正则表达式学习笔记 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含  有某种子串.将匹配的子串做替换或者从某个串中取出符合某个条件的子串等. 列目录时, dir *.txt或ls *.txt中的*.txt就不是一个正则表达式,因为这里*与正则式的*  的含义是不同的. 为便于理解和记忆,先从一些概念入手,所有特殊字符或字符组合有一个总表在后面,最后一  些例子供理解相应的概念. 正则表达式 是由普通字符(例如字符 a 到 z)以及特殊字符(

js正则表达式学习笔记

正则表达式:对字符串中的信息实现查找.替换和提取操作.(不支持注释和空白,必须写在一行内) 正则表达式的创建:包含在一对斜杠之间的字符(直接量语法) 例如: var pattern = /s$/; // 创建一个正则来匹配所有以字母s结尾的字符串,并赋值给pattern 一 .字符类 将直接量字符单独放进方括号内就组成了字符类. 一个字符类可以匹配它所包含的任意字符. 正则表达式的字符类: [...]      方括号内的任意字符 [^...]    不在方括号内的任意字符 .         

ruby语言学习笔记2

ruby学习笔记2  (摘自<ruby程序设计语言教程(中文版)>.pdf,全书25页) 1.同一个问题ruby有多个解决方案,途径 ruby之父:松本行弘(Matz),1993年创立 ruby的偶数发行版为稳定版 圆括号在方法调用中是可选的.即无论函数定义还是调用函数,括号都随意的 vim为ruby配置一个插件才好用:vim_ruby:在vimrc中添加<map> <F7> :!ruby -Ku % <CR>      表示用F7键运行  -Ku表示utf

python正则表达式学习笔记

就个人而言,主要用它来做一些复杂字符串分析,提取想要的信息 学习原则:够用就行,需要的时候在深入 现总结如下: 正则表达式中特殊的符号: "." 表任意字符 "^ " 表string起始 "$" 表string 结束 "*" "+" "?" 跟在字符后面表示,0个--多个, 1个--多个, 0个或者1个 *?, +?, ?? 符合条件的情况下,匹配的尽可能少//限制*,+,?匹配的贪婪性

java正则表达式学习笔记之命名捕获_java

很多正则引擎都支持命名分组,java是在java7中才引入这个特性,语法与.Net类似(.Net允许同一表达式出现名字相同的分组,java不允许). 命名分组很好理解,就是给分组进行命名.下面简单演示一下java中如何使用以及注意事项. 1.正则中定义名为NAME的分组(?<NAME>X) 这里X为我们要匹配的内容,注意,在这个命名不能重复,名字也不能以数字开头! 2.反向引用NAME组所匹配到的内容\k<NAME> 注意,反向引用是针对组所匹配到的内容,而非组的表达式. 3.替换

JavaScript正则表达式学习笔记与常用正则总结

一.RegExp ECMAScript通过RegExp类型类支持正则表达式,语法和Perl类似: var exp = /pattern/flags; patternb部分是任何简单的或复杂的正则表达式:flags是每个正则表达式所带的一个或者多个标志. 正则表达式的模式匹配支持三个标志: g:全局模式,即模式应用于整个字符串,而非在发现第一个匹配项时立即停止 i:不区分大小写模式 m:多行模式,即到达一行文本末尾是还会继续茶查找下一行中是否存在与模式匹配的项. 1.创建正则表达式 JavaScr

又一篇正则表达式学习笔记_正则表达式

1. \b:表示单词的开头或者结尾,可能是空格.标点符号或者换行,但是\b不匹配其中的任何一个,这是指代这些元素中的任一个位置. 示例:\bhi\b:查找文本中所有的"hi"单词,但是不包括him.history等字样 1.1 ^:匹配字符串的开始,特指一个段落的起始. 1.2 $:匹配字符串的结束.特指一个段落的结尾,这两个都是\b的子集. 重复: 2. *:表示*前面的内容重复出现任意多次,".*"连在一起就表示任意数量的不包含换行符的字符. 示例:\bhi\b