前瞻和查找背后的概念如何支持Ruby的正则表达式的零等宽度断言概念?(How the Look-ah

2019-07-18 08:32发布

我刚刚经历了概念了Zero-Width Assertions从文档。 而一些简单的问题来到我mind-

  • 为什么这样的名字Zero-Width Assertions
  • 该如何Look-aheadlook-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通过自己的举动产生输出-你能帮助我想象?

谢谢

Answer 1:

正则表达式从左至右匹配,并且移动一种“光标”沿着弦,因为他们去。 如果您正则表达式中包含一个普通字符像a ,这意味着:“如果有一个字母a在光标前,将光标移动提前一个字符,并坚持下去,否则,什么是错的;备份和尝试别的东西。” 所以,你可能会说, a有一个字符的“宽度”。

“零宽度断言”就是:它断言对字符串的东西(即,如果某些条件不成立不匹配),但它不会移动光标向前,因为它的“宽度”是零。

你可能已经熟悉了一些简单的零宽度断言,如^$ 。 这些匹配字符串的开始和结束。 如果光标不是在开始或结束时,看到那些符号,正则表达式引擎将失败,备份,并尝试别的东西。 但他们并没有真正移动光标向前,因为它们不匹配的字符; 他们只检查光标所在。

前瞻和回顾后的工作方式相同。 当正则表达式引擎试图与它们匹配,它会检查光标周围 ,看看是否正确的模式是超前的或后面,但在匹配的情况下,它不会移动光标。

考虑:

/(?=foo)foo/.match 'foo'

这将匹配! 正则表达式引擎是这样的:

  1. 开始在字符串的开头: |foo
  2. 正则表达式的第一部分是(?=foo) 。 这意味着:只有符合foo光标后出现。 可以? 嗯,是的,所以我们可以继续进行。 但光标不移动 ,因为这是零宽度。 我们还有|foo
  3. 其次是f 。 是否有一个f光标前面? 是的,所以出发,将光标移动过去ff|oo
  4. 其次是o 。 是否有一个o光标前面? 是的,所以出发,将光标移动过去ofo|o
  5. 再次同样的事情,使我们对foo|
  6. 我们到达了正则表达式的结束,并没有失败,所以该模式匹配。

在特别你的四个断言:

  • (?=...)是“期待”; 它声称, ... 确实光标之后出现。

     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"

是的,这些产生相同的输出。 这是使用向前看是有点棘手:

  1. 正则表达式引擎已尝试一些东西,但他们没有工作,而现在是在fores|ight
  2. 它检查(?!s) 光标之后的字符s ? 不,这是i ! 所以这部分匹配和匹配继续,但光标不移动 ,我们仍然有fores|ight
  3. 它检查ight 。 难道ight来光标之后? 嗯,是的,确实如此,那么移动光标: foresight|
  4. 我们就大功告成了!

将光标移到子串ight ,所以这是全场比赛,这就是被替换。

这样做(?!a)b是没用的,因为你说:下一个字符不能a ,而且必须 b 。 但是,这和上面一样匹配b

这可能是有用的时候,但是你需要一个更复杂的模式:例如, (?!3)\d会匹配任何数字不是3。

这是你想要什么:

1.9.3p125 :001 > "foresight".sub(/(?<!s)ight/, 'ee')
 => "foresight" 

此断言, s不来之前 ight



Answer 2:

零宽度断言是很难理解,直到你发现正则表达式匹配的位置以及字符。

当你看到字符串“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') 不,他们没有,所以一切都很好。



Answer 3:

零宽度断言的含义是消耗零个字符,同时匹配的表达式。 例如,在这个例子中,

"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?