Aru*_*hit 7 ruby regex ruby-1.9.3
我刚刚Zero-Width Assertions从文档中了解了这个概念.一些快速的问题进入我的脑海 -
Zero-Width Assertions?Look-ahead和look-behind概念支持这样的
Zero-Width Assertions概念??<=s,<!s,=s,<=s- 4符号指示模式里面?你能帮助我在这里专注于了解实际发生的事情我还尝试了一些微小的代码来理解逻辑,但对那些输出没有那么自信:
irb(main):001:0> "foresight".sub(/(?!s)ight/, 'ee')
=> "foresee"
irb(main):002:0> "foresight".sub(/(?=s)ight/, 'ee')
=> "foresight"
irb(main):003:0> "foresight".sub(/(?<=s)ight/, 'ee')
=> "foresee"
irb(main):004:0> "foresight".sub(/(?<!s)ight/, 'ee')
=> "foresight"
Run Code Online (Sandbox Code Playgroud)
谁能帮助我在这里理解?
编辑
在这里,我尝试了两个片段,其中"Zero-Width Assertions"概念如下:
irb(main):002:0> "foresight".sub(/(?!s)ight/, 'ee')
=> "foresee"
Run Code Online (Sandbox Code Playgroud)
另一个没有"零宽度断言"概念如下:
irb(main):003:0> "foresight".sub(/ight/, 'ee')
=> "foresee"
Run Code Online (Sandbox Code Playgroud)
以上两者都产生相同的输出,现在内部如何regexp通过它们自己移动来产生输出 - 你能帮我形象化吗?
谢谢
Eev*_*vee 18
正则表达式从左到右匹配,并在字符串移动时移动一种"光标".如果你的正则表达式包含一个常规字符a,这意味着:"如果a光标前面有一个字母,将光标向前移动一个字符,继续前进.否则,出错了;备份并尝试别的东西." 所以你可能会说它a有一个字符的"宽度".
"零宽度断言"就是这样:它断言某些字符串(即,如果某些条件不成立则不匹配),但它不会向前移动光标,因为它的"宽度"为零.
你可能已经熟悉了一些更简单的零宽度断言,比如^和$.这些匹配字符串的开头和结尾.如果光标在看到这些符号时不在开头或结尾,则正则表达式引擎将失败,备份并尝试其他操作.但它们实际上并没有向前移动光标,因为它们与字符不匹配; 它们只检查光标所在的位置.
Lookahead和lookbehind的工作方式相同.当正则表达式引擎尝试匹配它们时,它会检查光标周围以查看正确的模式是在其前面还是后面,但是在匹配的情况下,它不会移动光标.
考虑:
/(?=foo)foo/.match 'foo'
Run Code Online (Sandbox Code Playgroud)
这将匹配!正则表达式引擎是这样的:
|foo.(?=foo).这意味着:仅foo在光标后出现匹配.可以?嗯,是的,所以我们可以继续.但是光标不会移动,因为这是零宽度.我们还有|foo.f.是否有一个f光标前面?是的,所以继续,并将光标移过f:f|oo.o.是否有一个o光标前面?是的,所以继续,并将光标移过o:fo|o.foo|.特别是你的四个断言:
(?=...)是"向前看"; 它断言... 确实出现在光标之后.
1.9.3p125 :002 > 'jump june'.gsub(/ju(?=m)/, 'slu')
=> "slump june"
Run Code Online (Sandbox Code Playgroud)
"跳跃"中的"ju"匹配,因为接下来是"m".但是"六月"中的"ju"下一个没有"m",所以它一个人留下.
由于它不移动光标,因此在放置光标后必须小心. (?=a)b将永远不会匹配任何东西,因为它检查下一个字符是什么a,然后还检查相同的字符b,这是不可能的.
(?<=...)是"看守"; 它断言... 确实出现在光标之前.
1.9.3p125 :002 > 'four flour'.gsub(/(?<=f)our/, 'ive')
=> "five flour"
Run Code Online (Sandbox Code Playgroud)
"四"中的"我们"匹配,因为它前面有一个"f",但"面粉"中的"我们"在它之前有一个"l",所以它不匹配.
如上所述,你必须小心你摆在它面前的东西. a(?<=b)将永远不会匹配,因为它检查下一个字符是a,移动光标,然后检查前一个字符是什么b.
(?!...)是"消极的向前看"; 它声明在光标后... 没有出现.
1.9.3p125 :003 > 'child children'.gsub(/child(?!ren)/, 'kid')
=> "kid children"
Run Code Online (Sandbox Code Playgroud)
"孩子"匹配,因为接下来的是一个空间,而不是"仁"."孩子"没有.
这可能是我最常用的一个; 精细控制接下来不会发生的事情会派上用场.
(?<!...)是"消极的背后"; 它断言... 不会出现在光标之前.
1.9.3p125 :004 > 'foot root'.gsub(/(?<!r)oot/, 'eet')
=> "feet root"
Run Code Online (Sandbox Code Playgroud)
"脚"中的"oot"很好,因为之前没有"r"."根"中的"oot"显然具有"r".
作为附加限制,大多数正则表达式引擎要求...在这种情况下具有固定长度.所以,你不能使用?,+,*,或{n,m}.
你也可以嵌套这些,否则做各种疯狂的事情.我将它们主要用于一次性我知道我永远不需要维护,所以我没有任何实用应用程序的好例子; 老实说,他们很奇怪,你应该先尝试做一些你想做的事情.:)
事后想法:语法来自Perl正则表达式,它使用(?了大量扩展语法的各种符号,因为?它本身是无效的.所以<=并不意味着什么; (?<=是一个完整的标记,意思是"这是一个外观的开始".这是怎么样+=和++有独立的经营者,即使他们都开始+.
但它们很容易记住:=表示向前看(或者,真的,"在这里"),<表示向后看,并且!具有"不"的传统含义.
关于你后来的例子:
irb(main):002:0> "foresight".sub(/(?!s)ight/, 'ee')
=> "foresee"
irb(main):003:0> "foresight".sub(/ight/, 'ee')
=> "foresee"
Run Code Online (Sandbox Code Playgroud)
是的,这些产生相同的输出.使用前瞻这是一个棘手的问题:
fores|ight.(?!s).是字符后的光标s?不,是的i!因此该部分匹配并且匹配继续,但光标不移动,我们仍然有fores|ight.ight.ight光标之后会出现吗?嗯,是的,确实如此,所以移动光标:foresight|.光标移动到子字符串上ight,这样就完全匹配了,这就是被替换的内容.
这样做(?!a)b是没用的,因为你说:下一个字符不能是a,它必须是b.但这和匹配一样b!
这有时很有用,但是你需要一个更复杂的模式:例如,(?!3)\d匹配任何不是3的数字.
这就是你想要的:
1.9.3p125 :001 > "foresight".sub(/(?<!s)ight/, 'ee')
=> "foresight"
Run Code Online (Sandbox Code Playgroud)
这断言以前s没有. ight
在你意识到正则表达式匹配位置和字符之前,零宽度断言很难理解.
当你看到字符串"foo"时,你自然会读到三个字符.但是,还有四个位置,这里用管道标记:"| f | o | o |".前瞻或后视(aka lookarounds)匹配角色匹配表达式之前或之后的位置.
零宽度表达式与其他表达式之间的区别在于零宽度表达式仅匹配(或"消耗")位置.所以,例如:
/(app)apple/
Run Code Online (Sandbox Code Playgroud)
将无法匹配"苹果",因为它试图匹配"应用程序"两次.但
/(?=app)apple/
Run Code Online (Sandbox Code Playgroud)
将成功,因为前瞻只匹配"app"所遵循的位置.它实际上与"app"字符不匹配,允许下一个表达式使用它们.
LOOKAROUND说明
积极前瞻:
(?=s)想象一下,你是一名训练中士,你正在进行检查.你从这条线的前面开始,意图走过每个私人,并确保他们满足期望.但是,在这样做之前,你要一个接一个地向前看,以确保他们已经在房产订单中排队.私人的名字是"A","B","C","D"和"E".
/(?=ABCDE)...../.match('ABCDE').是的,他们都在场并且占了一席之地.否定前瞻:
(?!s)你在线下进行检查,最后站在私人D处.现在你要向前看,以确保来自另一家公司的"F"没有再次意外地陷入错误的阵型.
/.....(?!F)/.match('ABCDE').不,他这次没有滑倒,所以一切都很顺利.积极的观察:
(?<=s)完成检查后,中士在编队结束时.他转身扫描,确保没有人偷偷溜走.
/.....(?<=ABCDE)/.match('ABCDE').是的,每个人都在场,并且占了一席之地.负面观察:
(?<!s)最后,训练中士最后一次检查以确保私人A和B没有再次切换位置(因为他们喜欢KP).
/.....(?<!BACDE)/.match('ABCDE').不,他们没有,所以一切都很顺利.
| 归档时间: |
|
| 查看次数: |
971 次 |
| 最近记录: |