在Ruby的正则表达式中,向前看和向后看概念是如何支持这种零宽度断言概念的?

在Ruby的正则表达式中,向前看和向后看概念是如何支持这种零宽度断言概念的?,ruby,regex,ruby-1.9.3,Ruby,Regex,Ruby 1.9.3,我刚刚浏览了文档中的概念零宽度断言。我突然想到一些问题- 为什么这样的名称零宽度断言? 向前看和向后看概念是如何支持这样的 零宽度断言概念? 什么是这样的?=s,零宽度断言的含义是在匹配时使用零字符的表达式。比如说这个例子, "foresight".sub(/sight/, 'ee') "foresight".sub(/(?<=s)ight/, 'ee') 匹配的是 foresight ^^^^^ foresight ^^^^ 因此,结果将是 foreee f

我刚刚浏览了文档中的概念
零宽度断言
。我突然想到一些问题-

  • 为什么这样的名称
    零宽度断言
  • 向前看和向后看概念是如何支持这样的
    零宽度断言
    概念?
  • 什么是这样的
    ?=s
    零宽度断言的含义是在匹配时使用零字符的表达式。比如说这个例子,

    "foresight".sub(/sight/, 'ee')
    
    "foresight".sub(/(?<=s)ight/, 'ee')
    
    匹配的是

    foresight
        ^^^^^
    
    foresight
         ^^^^
    
    因此,结果将是

    foreee
    
    foresee
    
    但是在这个例子中,

    "foresight".sub(/sight/, 'ee')
    
    "foresight".sub(/(?<=s)ight/, 'ee')
    
    因此,结果将是

    foreee
    
    foresee
    
    零宽度断言的另一个示例是单词边界字符
    \b
    。例如,要匹配一个完整的单词,您可以尝试在单词周围加空格,例如

    得到

    flightdarkplight
    
    但是您看到了在替换过程中,匹配空格是如何删除它的吗?使用单词边界可以解决此问题:

    "flight light plight".sub(/\blight\b/, 'dark')
    
    \b
    匹配单词的开头或结尾,但实际上不匹配字符:宽度为零

    对于您的问题,最简洁的答案可能是:Lookahead和Lookahead断言是一种零宽度断言。所有前向和后向断言都是零宽度断言。


    以下是对您的示例的解释:

    irb(main):001:0> "foresight".sub(/(?!s)ight/, 'ee')
    => "foresee"
    
    irb(main):002:0> "foresight".sub(/(?!s)ight/, 'ee')
    => "foresee"
    
    irb(main):003:0> "foresight".sub(/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):003:0>“预见”。sub(/(?“预见”
    
    上面已经解释过了。(这是正确的。)

    irb(main):004:0>“预见”。sub(/(?“预见”
    

    在这种情况下,“firefaight”将取代“firefee”,但不是“foresight”来“prevent”。

    零宽度断言很难理解,除非您意识到正则表达式匹配位置和字符

    当你看到字符串“foo”时,你自然会读到三个字符。但是,还有四个位置,在这里用管道标记:“| f | o | o |”。“前看”或“后看”(又名“lookarounds”)与表达式前后字符的位置相匹配

    零宽度表达式与其他表达式的区别在于零宽度表达式仅匹配(或“使用”)位置。例如:

    /(app)apple/
    
    将无法匹配“apple”,因为它尝试匹配“app”两次。但是

    /(?=app)apple/
    
    将成功,因为前瞻只匹配“app”后面的位置。它实际上不匹配“app”字符,允许下一个表达式使用它们

    环顾说明

    正向前瞻:
    (?=s)

    假设你是一名训练中士,正在执行一项检查。你从队伍的最前面开始,目的是要经过每个士兵,确保他们达到预期。但是,在这样做之前,你要一个接一个地向前看,确保他们已按财产顺序排好队。士兵的名字是“a”、“B”、“C”、“D”和“E”.
    /(?=ABCDE)…/.match('ABCDE')
    。是的,它们都存在并说明了原因

    负前瞻:
    (?!s)

    你沿着生产线进行检查,最后站在D列兵处。现在你要向前看,确保另一家公司的“F”没有再次意外地进入错误的队形。
    /…(?!F)/.match('ABCDE')
    。不,他这次没有滑倒,所以一切都很好

    正向查找:
    (?
    最后,军士长最后看了一眼,以确保列兵A和B没有再次交换位置(因为他们喜欢KP)。
    /…(?。没有,他们没有,所以一切都很好


    正则表达式从左到右匹配,并沿字符串移动一种“光标”。如果正则表达式包含类似
    a
    的正则字符,则表示:“如果光标前面有一个字母
    a
    ,请将光标向前移动一个字符,然后继续移动。否则,会出现问题;请后退并尝试其他操作。”因此,您可以说
    a
    的“宽度”为一个字符

    “零宽度断言”就是这样的:它断言关于字符串的某些内容(即,如果某些条件不成立,则不匹配),但它不会向前移动光标,因为它的“宽度”为零

    您可能已经熟悉一些更简单的零宽度断言,如
    ^
    $
    。它们匹配字符串的开头和结尾。如果光标在看到这些符号时不在开头或结尾,正则表达式引擎将失败、备份并尝试其他操作。但它们实际上不会向前移动光标,因为它们不匹配字符;它们只检查光标所在的位置

    Lookahead和Lookahead的工作方式相同。当正则表达式引擎尝试匹配它们时,它会在光标周围检查正确的模式是在它前面还是后面,但如果匹配,它不会移动光标

    1.9.3p125 :002 > 'jump june'.gsub(/ju(?=m)/, 'slu')
     => "slump june" 
    
    1.9.3p125 :002 > 'four flour'.gsub(/(?<=f)our/, 'ive')
     => "five flour" 
    
    1.9.3p125 :003 > 'child children'.gsub(/child(?!ren)/, 'kid')
     => "kid children"
    
    1.9.3p125 :004 > 'foot root'.gsub(/(?<!r)oot/, 'eet')
     => "feet root" 
    
    考虑:

    /(?=foo)foo/.match 'foo'
    
    这将匹配!正则表达式引擎如下所示:

  • 从字符串开头开始:
    |foo
  • 正则表达式的第一部分是
    (?=foo)
    。这意味着:只有当
    foo
    出现在光标后时才匹配。是吗?好的,是的,所以我们可以继续。但是光标不移动,因为这是零宽度。我们仍然有
    |foo
    <
    irb(main):002:0> "foresight".sub(/(?!s)ight/, 'ee')
    => "foresee"
    
    irb(main):003:0> "foresight".sub(/ight/, 'ee')
    => "foresee"
    
    1.9.3p125 :001 > "foresight".sub(/(?<!s)ight/, 'ee')
     => "foresight"