赞 | 6 |
VIP | 4 |
好人卡 | 58 |
积分 | 5 |
经验 | 58579 |
最后登录 | 2024-6-30 |
在线时间 | 1478 小时 |
Lv2.观梦者
- 梦石
- 0
- 星屑
- 508
- 在线时间
- 1478 小时
- 注册时间
- 2011-9-17
- 帖子
- 1316
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 iisnow 于 2011-10-27 13:18 编辑
什么情况啊 “$~” 这个式子不能写嘛?,帖子怎么老异常啊
哎,上一节索引:
正则表达式之准新手脚本教学(略浅)(1)
我果然还是很闲啊,今天就接着2吧
接上一次讲都字符簇,今天再提几点:
1.字符簇归根结底只能匹配一个字符
/r[a\so]m/ #就是说它能匹配 "ram"、"r m"、"rom" 三种字符段
再复杂也只能匹配一个!集合中的一个字符
2.字符簇中的范围a-p,4-9,,等等均不能倒过来写 = =
即不能为p-a,6-3(RUBY没有那么智能),另外大小写不能混用(大家不要看着我全部是小写就忘记大写了= =)
虽然在范围类里面"A".."b"指ASCII码在A与b之间的字符,不仅有字母,还有一些其他字符,故建议不要采用
(这句话刚改,前面说错了)
对于空集而言,字符簇是支持的:
/[A-N&&Q-VY]/ 就是空集,不匹配任何字符(这pattern就没意义了= =)
空集不等于没有 = = ,
/c[A-N&&Q-VY]t/ 就不匹配 "ct"
3.单引号字符串与双引号字符串差别主要在控制码的转义方面,就是说单引号中的转义字符不会被转义(而"\"会占一个字符位)- 'iis\now' == "iis\\now" #=> true
复制代码 所以不会与pattern中的转义字符匹配
(可以用 p 与 print 方法分别输出 'y\nm' 与 "y\nm" 试试):
例如:- iisnow = /\n/
- haha = /s/
- s1 = 'hu\ns'
- s2 = "hu\ns"
- p iisnow =~ s1
- p iisnow =~ s2
- p haha =~ s1
- p haha =~ s2
- exit
复制代码 (另外\t\s均无法匹配单引号字符串中的控制码,但是\s可以匹配半角空格)
4.字符簇常用小技巧:
匹配非此字符: /[^s]/
匹配大小写的某字符: /[aA]/
……然后就不知道了 = =
本节主要内容:
1.转义字符及控制码
2.重复pattern及量词
3.MatchData类概述
4.贪婪
好吧,接着讲正文吧:
昨天说了要一下讲控制码,今天就先说了吧,顺便也把一些其他元字符说一下:
\n 匹配换行符
\t 匹配TAB
\s 空字符 相当于[ \t\r\n\f](最前面有个空格,是半角的,可以写作[[:space:]] )
( \r 回车符、返回效果同\n差不多
\f 换页 均不常用 , 在处理文件的时候可能遇到)
\w 任意字母、数字外加下划线 [_0-9a-zA-Z](也可写作 [[:alnum:]] )
\d 任意数字 [0-9](也可写作 [[:digit:]] )
\W ^\w
\D ^\d
\S ^\s (可以写作 [[:graph:]] )
括号中的写法为POSIX表达式,仅供了解:(写的时候注意两个[[]],且不能往里面再加什么了)
[[:alpha:]] 等同于 [a-zA-Z]
[[:blank:]] 等同于 [ \t]
[[:cntrl:]] 匹配所以转义字符
[[:lower:]] 等同于 [a-z]
[[:upper:]] 自己配合上面那个理解
[[:print:]] 等同于 [[:graph:]] + 空格
[[:punct:]] 匹配标点符号
(上述所有的东西,都只匹配一个字符)
好了,下面开始新的东西的
讲完[] , 再说说 ?吧(那么肯定会讲 * + 咯~)
不过这之前把简单的 . 解决吧
. 等同于 ^\n (over)
重复:
话说[]怎么匹配也只能匹配一个字符,那倘若我要匹配的字符我也不清楚有多少个怎么办呢?
下面的就 介绍几个量词,(类似于指数,用在字符之后)
? 表示0次或1次
+ 表示一次或多次
* 表示0次或多次(当然1次也算)
这些符号用于单个字符的后面,即对它的紧挨着的前一个字符修饰- /i+sb?[no]*/ =~ "iisnow" #=> 0
复制代码 (注意,字符簇当做一个字符处理,我说的很多遍了)
这些字符均不能单独使用即
/*/ /?/ /+/ 都是错误的,要想匹配那个字符要转义
但是倘若用在[]里面就不需要转义,但加上"\"也行,一样的- /a\*/ =~ "*aaa*" #=> 3
- /[a*]/ =~ "*aaa*" #=> 0
- /[a\*]/ =~ "\*aa*" #=> 0
- # 不要误解上式,双引号字符串中 "\*" 转义为 "*" 字符,并不是匹配了"\",不信:
- /[a\*]/ =~ '\*aa*' #=> 1
复制代码 当然上述的多次可以更加具体一些:
需要用到"{" "}"
{a} 前一字符必须出现a次
{a,b} 前一字符出现次数 ∈ [a,b] (额,数学式子,别理解成pattern了)
上式中a缺失 用0代替 b缺失 用+∞代替 你们懂的
于是
上述的 {a} 等同于 b = a 的情况(允许a = b但是b < a就错了)
? 等同于 {0,1} + 等同于 {1,} * 等同于 {0,}
若a,b同时缺失:注意{,}匹配三个字符
({}两个字符不需要转义,所以/{}/ 就是匹配 "{}" 的 写的时候别忘记的闭括号)
量词可以叠加使用(不过这样做很闲)
首先说简单的
/a+*/ 匹配效果与 /a*/ 无异
/a?+/ 匹配效果与 /a*/ 无异
可以无限的加/a?++*+++*++++/ (= =)
但是注意一点
/a+?/ /a*?/ /a??/ 就不是你们想的那个意思了,下面再讲有关"贪婪"的概念
复杂一点的 {} 的叠加(这样更闲 = =)
于是我们一起探索一下:- /a{1,2}{2,3}/ =~ "abaabaaabaaaabaaaaabaaaaaa" #=> 2
- /a{2}{2,3}/ =~ "abaabaaabaaaabaaaaabaaaaaa" #=> 9
- # 于是是不是说,把范围相乘就行了呢:
- /soi{2,3}{2}/ =~ "soiiiisoiisoii"
- #你们说它返回多少:0?
- #其实是5,就是说它匹配soiisoii
- /soi{2,3}{2}{2,3}/ =~ "soiiiiiiiisoiisoiisoiisoii" #=> 10
- /i{2,3}snow{2,3}{2}/ =~ "iisnowwiisnowwiisnowwsnoww" #=> 14
- /i{2,3}snow{2,3}{2}{2}/ =~ "iisnowwiisnowwiisnowwsnowwsnowwsnoww" #=> 14
- /i{2,3}s*now{2,3}{2}{2}/ =~ "iisnowwnowwnowwnowwnowwnowwnowwnoww" #=> 0
复制代码 规律总结一下:(话说大家实验的时候不需要想我这样写这么长……我只是自恋而已)
{}连用时,除第一个{}修饰一个字符外,后面叠加的修饰它前面的所有字符(直到遇到上一个与之没有连用的重复量词)组成的字符段
至于?、+、*与{}的混用,请大家按类似的方法探索,实际上也没什么探索的了……
但是希望大家学会这种方式
下面讲贪婪的问题之前,写要讲一下
.match方法的返回值的问题:
MatchDate类
(有于代码中的变量$~ $& $` $1 $2 使帖子出现问题,后面就不用代码框了,改成蓝色,鬼知道怎么回事啊)
它是一种特殊的变量类型,专为Regexp量身订做的类
用.match或.last_match得到
它包含了字符串中所有与pattern相匹配的字符段
新版本1.9.2可以通过p 方法看到字符段,但是RM不行,还是只有ID
p "snwsow".match(/s[no]w/) #=> #<MatchData: ********>
而last_match方法引用要按照下述形式
/s[no]w/ =~ "snwsow"
p Regexp.last_match # => #<MatchData:********>
# 而真正想要调用出它包含的匹配的字符段:
# MatchData可以像数组那样用索引调用
snow = "snwsow".match(/s[no]w/)
p snow[0] #=> "snw"
(有同学问:snow[1]怎么是nil,这以后再说,有关组群)
当然还有另一种返回结果的方式:
MatchData在返回结果时,将结果全部存放在 $~ $& $` $1 $2 ……这些奇形怪状变量之中
但是他们都不是全局变量啊,注意
其中 $~ 结果为 #<MatchData:********> 等同于上述的那些做法
$& 等同于 那个snow[0]
$' 返回原字符串截掉被$&以及它前方的字符,即还未被匹配过的字符段
snow = "kusnwdesow".match(/s[no]w/)
p $' #=> "desow"
# 通过这个变量可以实现得到全部匹配段,用循环试一下
$n 等同与 那个snow[n](n为正整数)
$` 这个返回已匹配过的字符段中不匹配的部分
(右边的字符为1左边的那个键,英文输入法下面为 "`" )
snow = "kusnwdesow".match(/s[no]w/)
p $` #=> "ku"
这些变量每执行一次匹配就会改变,即反映最近一次匹配结果的值
snow = "kusnwdesow".match(/s[no]w/)
p $` #=> "ku"
snow = "asdfsnwdesow".match(/s[no]w/)
p $` #=> "asdf"
知道了这些,我们开始讲贪恋吧!!
我们先把匹配过程看成是pattern“吃”字符串的过程,每个pattern喜欢吃的东西都不一样,看它长的什么样就知道它喜欢吃什么
/a/ 很挑食,它只吃 "a" 这个字符
/[ab]/ 说我a,b通吃
/aaa/ 说我喜欢3个a一齐吃,少一个都不行
/a+b/
说我先找a要是发现a了就往后面找,第一个不是a的字符要是b的话,刚才的所有a和这个b我全吃了
(这就是一种意义上的“贪婪”,但不是我们要讲的)
就是说所有的a全部都被匹配
isnow = "kaaabhbi".match(/a+b/)
p $& #=> "aaab"
倘若纯在字符簇、转义符就跟贪婪了
/.+b/
说我先把字符串分成一行行的,哪行的结尾要是b,我就把那行全吃了(这样讲好像不对……真的不对…= =)
这样说才对:我先找第一个不是换行符的字符,然后往后面找,只要不是换行符就继续找,找到了b之后,把刚才的全吃了,(嗯,是不是吃的太少了)不死心,看看后面要还不是换行符就继续找,要是惊奇的发现居然还可以吃,就继续吃
(这只是一顿哦,哦呵呵呵)
iisnow = "kacabhbi".match(/a.+b/)
p $& #=> "acabhb"
(在第一个b那里并不会停,会继续检阅后面,这是另一种贪婪)
当然{}也是贪婪的:
iisnow = "kaaabhbi".match(/a.{2,10}b/)
p $& #=> "aaabhb"
第一种“贪婪”并不是我们要谈论的,并且也不可避免
后一种才是我们的主题:
比如我不希望它贪婪怎么办,?帮你解决!
只要在它后面加一个?即可使它不再贪婪(现实世界这样多好啊)
iisnow = "kacabhbi".match(/a.+?b/)
p $& #=> "acab"
iisnow = "kaaabhbi".match(/a.{2,10}?b/)
p $& #=> "aaab"
混用的时候当然大家就要注意了
OK,这一节就这么多啦
最后希望大家认真考虑 用 $` 得到所有匹配段的方法,其实挺简单的~~
(是不是要禁用编辑器代码啊……= =)
第3节
我就不另开新帖了,就这样更新吧!
我不敢连贴啊……还不是因为没有人回复,哼哼
上一次,我们匆匆讲完了贪婪的故事
(这个术语,MS在RUBY中叫贪婪,而在其他语言中就不这样叫)
这次再说一下:
1.这是一个很早就想说的问题
.match方法不一定要
string.match(pattern)的形式
pattern.match(string)是一样的
2.不贪婪即是在匹配时尽可能的少的匹配字符段,尽可能少,前提是能匹配:不可能因为为了匹配上最后一个字符"w"必须要把"sno"全部匹配上显得很贪婪而不进行匹配
但假如:- /i[snow]+?w/ =~ "iisnowsnow"
复制代码 这时候就只会匹配到第一个"w"。
(这其实要从贪婪过程的本质——回溯说起!
回溯过程其实还是略显复杂的,
用我的话说就是:
要是不贪婪最后居然导致什么都吃不到了,还不如贪婪一下呢,但是会克制一下的!)
3.上一次说到的POSIX表达式,大家要是试过就知道,要是少了一对[]会怎么样?
/[:digit:]/ =~ "iisnow3" 它会匹配什么?
4.关于.的问题
当.用于[]中时,将会只匹配"."这个字符
而多数情况下 . 是不会放入[]中的,(因为它能匹配的已经够多了)
故[.\n]并不是匹配所有字符的意思,真的要匹配所有字符
/./m
即可,m是一种修饰符,估计下一讲再讲吧~
5.上一次提出的,得到所有匹配段的问题,参考脚本如下:
pattern = /[huaid]f/
string = "hfujifhbdfffaf"
string.match(pattern)
para = []
loop do
para << $& if $& != nil
break if $& == nil
pattern =~ $'
end
p para
(出现了$1等等的代码段,就用蓝色代替,其他的照常)
当然啦,可以有很多种写法的,你的能否实现功能就行了啊…下面的实验就用这个表达式吧,(当然,要是嫌RM的标题画面什么的烦人的话,把代码插到Main的首行,后面再加一句:exit
就可以啦~~)
本节内容:
1.定位符
2.捕获与引用
3.选择
我们有时候会遇到这样的要求:
匹配一个以"i"开头以"w"结尾的字符串
也许你说:
/i.*w/
就可以啊……
"okaokdofkaoiisnowanosidjfoiasjdofi"
可以吗?
于是怎么去定义让i位于字符串首位,让w位于字符串末尾:
现在我们就说说几个0位匹配符(就是说不占字符位置的匹配符)
又称定位符(Anchors)
^ 前面就介绍过(在[]里面,意义是取反)
而今天它的位置也是用在开头,用于匹配字符串的开头
相应的 $ 就是匹配字符串的末尾了:
/^i.*w$/就可以完成上述功能了:但是大家要注意一点,我曾经说过在[]要是不用在首位,就匹配自身"^"的!
现在要是我们不把它放在pattern的首位呢?
/i^kiss^you/ =~ "i^kiss^you"
p $& #=> nil
所以说,在字符簇外面,有些特权是行不通的,转义吧!
/i\^kiss\^you/ =~ "i^kiss^you"
p $& #=> "i^kiss^you"
要是你把^不转义的放pattern的中间,是不是就不可能匹配都string呢?就可以匹配,就是说^只是匹配一个位置而已,放哪里都是匹配那个位置
$和^的情况是一样的,大家请自行验证
注意
当一个字符串是多行的话,每一行的开头与结尾均是会被^与$匹配的
/^i.*w$/ =~ "iisnow\nihatesnow"
p $& #=> "iisnow"
使用那个代码(显示所有匹配段的代码)会发现后面的"ihatesnow"也可以被匹配
于是就有一个问题:结尾、开头以及换行符的顺序问题:
/w$\n^i/ =~ "iisnow\nihatesnow"
p $& #=> "w\ni"
清楚了吧~
要是说想要匹配整段,换行符的左右的头尾忽略掉,要怎么办
在pattern后面加一个"m"试试:
/^i.*w$/m =~ "iisnow\nihatesnow"
p $& #=> "iisnow\nihatesnow"
(但是实际上并不是依靠忽略换行符的左右的头尾,而是通过*的贪婪来实现的
不信:
/^i.*?w$/m =~ "iisnow\nihatesnow"
p $& #=> "iisnow"
而修饰符m的作用只不过就是将"\n"列入了被"."匹配的行列中而已
)
还有一种方法,便是使用\A和\z(注意是小写,大写的话后面说)
其实\A与\z,类似于^和$,匹配开头和结尾(意义很好理解哦~~)
但是区别还是有的:对比一下
/^i.*w$/ =~ "iisnow\nihatesnow"
p $& #=> "iisnow"
/\Ai.*w\z/ =~ "iisnow\nihatesnow"
p $& #=> nil
/^i.*?w$/m =~ "iisnow\nihatesnow"
p $& #=> "iisnow"
/\Ai.*?w\z/m =~ "iisnow\nihatesnow"
p $& #=> "iisnow\nihatesnow"
知道差别了吧,\A和\z才是真正的字符串的首位末尾匹配!
另外\Z就是和$一样了,遇到换行就认了…
(有人问\a是什么……是什么?我怎么知道……~~~~)
有时候我们想的并不是一段话的首位末尾的操作,我们只想看看文中处没有出现一个单词怎么办?
比如:- string =<<EOF
- We call Ruby a transparent language. By that we mean that Ruby doesn't obscure
- the solutions you write behind lots of syntax and the need to churn out reams
- of support code just to get simple things done. With Ruby you write programs
- close to the problem domain. Rather than constantly mapping your ideas and
- designs down to the pedestrian level of most languages, with Ruby you'll find
- you can express them directly and express them elegantly. This means you code
- faster. It also means your programs stay readable and maintainable.
- EOF
复制代码 这样的一个字符串中
(不知道多行字符串输入的同志,看看帮助吧,真不懂的话,
p string
看string是什么,有个大致的概念,以后我们再讲多行字符串输入吧)
匹配所有的 at 单词
事实上,里面没有at这个单词= =,那么为什么还是告诉我们,匹配了3个呢?
因为像that这种杂货混在里面了……怎么办
\b帮你解决,类似于$与^的零字符匹配,\b匹配的是单词的边界
即一个字符与空格或换行符的交界处,理解了$、^了之后,\b应该不难理解
\B就是\b的反面,即非单词边界
/\biisnow/就是指以iisnow开头的单词
/iisnow\b/就是指以iisnow结尾的单词
/\biisnow\b/就是专指iisnow这个单词了(实际上iisnow不是个单词= =)
下面转一下折(其实这句话用来承上启下很简洁,不是吗?)
到今天为止讲完了"[]"的字符簇,"{}"在重复时的应用,现在讲讲
"()"的用法:
他们的功能很多,一个个的说吧:
事先来个大致的了解吧:
/i(is)(no)w/ =~ "iisnow"
p $& #=> "iisnow"
看起来,貌似没有什么区别啊!
恩~那么
p $1 #=> "is"
p $2 #=> "no"
试试:
怎么样昨天说的改一下:
snow = "snwsow".match(/s([no])w/)
p snow[0] #=> "snw"
p snow[1] #=> "n"
p snow[2] #=> nil
就是说()对应的段所匹配的字符段会赋值给$1,$2,$3……
(其实这很方便我们研究的)
第一个()对应$1,依次类推,没有就nil
()的这种功能叫“捕获”(Capturing)
捕获的字符段有什么用呢?用于确定字符块、还有用于引用!
确定字符块,
重复量词的作用域只有一个字符,但是()也可以跟上
代表()内所有表达式的重复修饰:
/(is)+(sb)*(now)?(hah){1,4}/ =~ "isisnowhahhah"
p $& #=> "isisnowhahhah"
p $1 #=> "is"
p $2 #=> nil
p $3 #=> "now"
p $4 #=> "hah"
再说说引用吧
有时候我们需要这样很强限制的匹配
/s..ws..w/
我们想要它匹配类似于
snowsnow
snuwsnuw
snmwsnmw
的字符串
而不要匹配
snowsnuw
snowsnmw
的字符串怎么办?
直接这样肯定不行:- /s(..)ws(..)w/ =~ "snowsnowsnowsnuwsnuwsnuw"
复制代码 得到的para(前面的代码啊!)
["snowsnow","snowsnuw","snuwsnuw"]
似乎ms有点难度
而引用就可以解决这一点:使用\配上数字进行引用- /s(..)ws\1w/ =~ "snowsnowsnowsnuwsnuwsnuw"
复制代码 para 就是 ["snowsnow","snuwsnuw"]了
这就是引用的作用
即\1会引用第一个()所匹配的字符段放在\1所在的位置!
同理\2,\3就都懂了吧!
引用同样可以用重复量词修饰:
/i(.)\1*w/ =~ "innnw"
p $& #=> "innnw"
p $1 #=> "n"
当引用遇上贪婪:- /s(.+)(.*)ws\1w/ =~ "snowsnowsnowsnuwsnuwsnuwsnowsnuwsnow"
- /s(.+?)(.*?)ws\1w/ =~ "snowsnowsnowsnuwsnuwsnuwsnowsnuwsnow"
复制代码 其实原则就是尽量贪婪(或不贪婪)前提是要匹配得上,结合引用一起考虑即可
引用不止可以用1、2、3、4这几个俗套的名字,在很长的pattern里面,要引用前面所捕获的内容,没有标示名,是会很蛋疼的,于是乎,()是可以命名的!
命名方法在括号最前面加上 ?<> <>里面写上标示名称即可
后面引用的话使用\k<> <>里面写上同样的标示名称即可- /(?<name>\w+):(?<age>\d+)\n\k<age>\k<name>/ =~ "iisnow:99\n99iisnow"
复制代码 (我才不会告诉你们我多少岁了呢~)
(下面讲讲一下RM用不上的功能!)
而后面要调用匹配捕获的结果:- iisnow = "iisnow:99\n99iisnow".match(/(?<name>\w+):(?<age>\d+)\n\k<age>\k<name>/)
- p iisnow[0]
- p iisnow[1]
- p iisnow[2]
复制代码 都是可以显示的,但是那要标示名称干什么?
p iisnow[:name]
p iisnow[:age]
(MS RM是不支持的,会报错)
下面回归正题:()的嵌套以及子表达式的引用
当()嵌套使用时,引用的次序不管是不是子(),从左往右按"("的次序排列
/i(..(..))w/ =~ "iisnow"
p $& #=> "iisnow"
p $1 #=> "isno"
p $2 #=> "no"
(下面又是RM不支持的功能)
如果在括号中引用该括号的捕获内容会怎么样?
如:
/i(.\1)w/ 这似乎就成了无限的循环了……是吧,有什么办法实现这种引用呢?
\g就可以
/i(.\g)w/ 就可以,既然不支持,就不多说了
再说说()的另一个功能吧:
选择(Alternation),需要用到 | 操作符- /i(is|are)now/ =~ "iisnowiarenow"
复制代码 使用最开始的那个代码:para就为:
["iisnow"、"iarenow"]
其实就是说 | 操作符对字符段进行了OR的操作,类似于[]字符簇的功能,只是它是按字符段来进行OR的,当然 | 可以连用: /i(is|are|was|were|will)now/ 均可
但是注意,这样的选择,不同于捕获,是不会给$1等等赋值的!
/i(is|are)now/ =~ "iisnowiarenow"
p $1 #=> nil
今天就这么多吧,好了,下次再讲吧,
|
评分
-
查看全部评分
|