我刚刚经历了概念了Zero-Width Assertions
从文档。 而一些简单的问题来到我mind-
- 为什么这样的名字
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"
谁能帮我在这里明白了吗?
编辑
在这里,我已经尝试了两种片段用一个“零宽度断言”概念如下:
irb(main):002:0> "foresight".sub(/(?!s)ight/, 'ee')
=> "foresee"
另一种是没有“零宽度断言”概念如下:
irb(main):003:0> "foresight".sub(/ight/, 'ee')
=> "foresee"
上述两个产生相同的输出,现在在内部如何既regexp
通过自己的举动产生输出-你能帮助我想象?
谢谢
正则表达式从左至右匹配,并且移动一种“光标”沿着弦,因为他们去。 如果您正则表达式中包含一个普通字符像a
,这意味着:“如果有一个字母a
在光标前,将光标移动提前一个字符,并坚持下去,否则,什么是错的;备份和尝试别的东西。” 所以,你可能会说, a
有一个字符的“宽度”。
“零宽度断言”就是:它断言对字符串的东西(即,如果某些条件不成立不匹配),但它不会移动光标向前,因为它的“宽度”是零。
你可能已经熟悉了一些简单的零宽度断言,如^
和$
。 这些匹配字符串的开始和结束。 如果光标不是在开始或结束时,看到那些符号,正则表达式引擎将失败,备份,并尝试别的东西。 但他们并没有真正移动光标向前,因为它们不匹配的字符; 他们只检查光标所在。
前瞻和回顾后的工作方式相同。 当正则表达式引擎试图与它们匹配,它会检查光标周围 ,看看是否正确的模式是超前的或后面,但在匹配的情况下,它不会移动光标。
考虑:
/(?=foo)foo/.match 'foo'
这将匹配! 正则表达式引擎是这样的:
- 开始在字符串的开头:
|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"
在“菊”中的“跳”相匹配,因为“M”随之而来的。 但是,“菊”,在“六一”不具有“m”在旁边,所以它单独留在家中。
因为它不移动光标,你有后放置任何东西时要小心。 (?=a)b
永远不会匹配任何东西,因为它会检查下一个字符是a
,则还检查相同的字符是b
,这是不可能的。
(?<=...)
是“回顾后”; 它声称, ...
请问光标之前出现。
1.9.3p125 :002 > 'four flour'.gsub(/(?<=f)our/, 'ive') => "five flour"
在“我们”中的“四”匹配的,因为有一个“F”之前立即,但“我们”,在“面粉”有一个“L”之前它,所以它不匹配。
和上面一样,你必须要小心你面前放什么。 a(?<=b)
永远不会匹配,因为它会检查下一个字符是a
,移动光标,然后检查该先前字符是b
。
(?!...)
是“负前瞻”; 它声称, ...
不光标之后出现。
1.9.3p125 :003 > 'child children'.gsub(/child(?!ren)/, 'kid') => "kid children"
“孩子”相匹配,因为随之而来的是一个空间,而不是“仁”。 “孩子”却没有。
这可能是一个我得到尽可能使用; 精细地控制什么不能来下就派上用场了。
(?<!...)
是“负回顾后”; 它声称, ...
不光标之前出现。
1.9.3p125 :004 > 'foot root'.gsub(/(?<!r)oot/, 'eet') => "feet root"
该“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"
是的,这些产生相同的输出。 这是使用向前看是有点棘手:
- 正则表达式引擎已尝试一些东西,但他们没有工作,而现在是在
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"
此断言, s
不来之前 ight
。
零宽度断言是很难理解,直到你发现正则表达式匹配的位置以及字符。
当你看到字符串“foo”你自然看三个字符。 但是,也有四个位置 ,通过管道这里标明: “| F | O | O |”。 先行或回顾后(又名lookarounds)匹配的位置处表达之前或之后匹配的字符。
零宽度的表达与其他表现形式之间的差异在于,零宽度表达只匹配(或“消耗”)的位置。 因此,举例来说:
/(app)apple/
将无法匹配“苹果”,因为它试图匹配“应用”两次。 但
/(?=app)apple/
会成功,因为先行只匹配,其中“应用程序”下面的位置 。 实际上它并不匹配“应用程序”字,让下一个表达式来使用它们。
环视功能描述
正前瞻: (?=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')
不,他们没有,所以一切都很好。
零宽度断言的含义是消耗零个字符,同时匹配的表达式。 例如,在这个例子中,
"foresight".sub(/sight/, 'ee')
什么是匹配的是
foresight
^^^^^
因此结果将是
foreee
然而,在这个例子中,
"foresight".sub(/(?<=s)ight/, 'ee')
什么是匹配的是
foresight
^^^^
因此,结果将是
foresee
零宽度断言的另一个例子是字边界字符, \b
。 例如,匹配一个完整的单词,你可以尝试与周围空间的字, 如
"flight light plight".sub(/\slight\s/, 'dark')
要得到
flightdarkplight
但是你看替换时如何匹配空格去掉呢? 使用单词边界来解决这个问题:
"flight light plight".sub(/\blight\b/, 'dark')
该\b
单词的开头或结尾匹配,但实际上并不匹配字符:它是零宽度 。
也许最简洁的回答你的问题是这样的: 前瞻和后向断言是一类零宽度断言。 所有先行和向后断言是零宽度断言。
这里是你的例子说明:
irb(main):001:0> "foresight".sub(/(?!s)ight/, 'ee')
=> "foresee"
上面,你说,“比赛下一个字符是不是一个s
,再一个i
。” 这是永远对的i
,由于i
从来都不是s
,所以替代成功。
irb(main):002:0> "foresight".sub(/(?=s)ight/, 'ee')
=> "foresight"
上面,你说,“比赛下一个字符是一个s
,再一个i
。” 这是不正确的,因为一个i
从来都不是一个s
的,所以替换将失败。
irb(main):003:0> "foresight".sub(/(?<=s)ight/, 'ee')
=> "foresee"
上面已经解释。 (这是正确的)。
irb(main):004:0> "foresight".sub(/(?<!s)ight/, 'ee')
=> "foresight"
以上,现在应该清楚了。 在这种情况下,“交火”将替换为“firefee”,而不是“先见之明”,以“先知先觉”。
文章来源: How the Look-ahead and Look-behind concept supports such Zero-Width Assertions concept in Regex of Ruby?