ruby - 在 Ruby 的正则表达式中,前瞻和后视概念如何支持这种零宽度断言概念?

标签 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"
    

    任何人都可以帮我理解吗?

    编辑

    在这里,我尝试了两个带有“零宽度断言”概念的片段,如下所示:
    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" 
    

    “jump”中的“ju”匹配,因为接下来是“m”。但是“june”中的“ju”下一个没有“m”,所以它是单独存在的。

    由于它不会移动光标,因此在其后放置任何内容时必须小心。 (?=a)b永远不会匹配任何东西,因为它检查下一个字符是 a ,然后还要检查相同的字符是否为 b ,这是不可能的。
  • (?<=...)是“后视”;它断言 ...确实出现在光标之前。
    1.9.3p125 :002 > 'four flour'.gsub(/(?<=f)our/, 'ive')
     => "five flour" 
    

    “four”中的“our”匹配,因为在它之前有一个“f”,但“flour”中的“our”在它之前有一个“l”,所以它不匹配。

    像上面一样,您必须小心放置在它之前的内容。 a(?<=b)永远不会匹配,因为它检查下一个字符是 a , 移动光标,然后检查前一个字符是否为 b .
  • (?!...)是“负前瞻”;它断言 ...不会出现在光标之后。
    1.9.3p125 :003 > 'child children'.gsub(/child(?!ren)/, 'kid')
     => "kid children"
    

    “child”匹配,因为接下来是一个空格,而不是“ren”。 “ child ”没有。

    这可能是我最常用的一种;精细控制接下来不能发生的事情就派上用场了。
  • (?<!...)是“负面回顾”;它断言 ...不会出现在光标之前。
    1.9.3p125 :004 > 'foot root'.gsub(/(?<!r)oot/, 'eet')
     => "feet root" 
    

    “foot”中的“oot”很好,因为它前面没有“r”。 “root”中的“oot”显然有一个“r”。

    作为额外的限制,大多数正则表达式引擎要求 ...在这种情况下具有固定长度。所以你不能使用 ? , + , * , 或 {n,m} .

  • 你也可以嵌套这些,否则做各种疯狂的事情。我主要将它们用于我知道永远不必维护的一次性使用,因此我没有任何实用的实际应用程序示例;老实说,它们很奇怪,您应该先尝试以其他方式做您想做的事。 :)

    事后思考:语法来自 Perl regular expressions ,其中使用了 (?后面是许多扩展语法的各种符号,因为 ?本身是无效的。所以<=本身没有任何意义; (?<=是一个完整的标记,意思是“这是回顾的开始”。就像如何+=++是独立的运算符,即使它们都以 + 开头.

    不过,它们很容易记住:=表示向前看(或者,实际上,“这里”),<表示向后看,!有“不”的传统含义。

    关于你后面的例子:
    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 .

    关于ruby - 在 Ruby 的正则表达式中,前瞻和后视概念如何支持这种零宽度断言概念?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14387631/

    相关文章:

    ruby - 如何在 IRB 中重新加载脚本?

    java - 用于在特定标题下获取特定字符串的正则表达式

    jQuery:用跨度包裹文本的一部分

    ruby-on-rails - 无法在 Rails 中使用 ActiveRecord 和 PostgreSQL 从每个组中选择不同的行

    ruby-on-rails - 如何在 Rails 模型中获取完整的 Assets 路径

    ruby-on-rails - 工厂女工与制造的比较

    java - 如何撤消正则表达式执行的替换?

    mysql - 对从 ActiveRecord 返回的结果强制排序

    ruby - 无法安装 ruby​​-debug,错误 : rb_method_entry_t. called_id

    ruby - 我们可以一起使用 selenium-webdriver 和 nokogiri 吗?