如何在Ruby中使用%r<...>定界的正则表达式进行负向后查找?

10

我喜欢使用%r<…>作为分隔符,因为这样可以很容易地找到正则表达式的开头和结尾,并且我不必转义任何/。但似乎它们有其他分隔符没有的难以克服的限制?

可以使用任何其他可能的分隔符:

/(?<!foo)/
%r{(?<!foo)}
%r[(?<!foo)]
%r|(?<!foo)|
%r/(?<!foo)/

但是当我尝试这样做时:

%r<(?<!foo)>

它会给出这个语法错误:

unterminated regexp meets end of file

好的,它可能不喜欢它不是一个平衡对,但是你如何转义它,使它喜欢呢?

需要转义吗?

根据 wikibooks.org

任何单个非字母数字字符都可以用作分隔符, %[包括这些],%?还是这些?,%~甚至这些~。 使用此表示法,通常的字符串分隔符"和'可以出现在未转义的字符串中, 但是您选择的新分隔符确实需要转义。

确实,在这些示例中需要转义:

%r!(?<\!foo)!                                                             
%r?(\?<!foo)? 

但如果那是唯一的问题,那么我应该可以像这样逃脱它并使其起作用:

%r<(?\<!foo)>

但会产生这个错误:

undefined group option: /(?\<!foo)/

那么也许不需要/允许转义?wikibooks.org%<pointy brackets>列为其中一种例外:

然而,如果你使用%(parentheses), %[square brackets], %{curly brackets}%<pointy brackets>作为分隔符,则只要它们成对出现且平衡,这些分隔符就可以在字符串中出现未转义

平衡的配对是否会造成问题?

只要你在正则表达式中需要它们,比如...

%r{(?<!foo{1})}   # repetition quantifier
%r[(?<![foo])]    # character class
%r<(?<name>foo)>  # named capture group

但是如果你需要在正则表达式内部插入左侧定界符({、[或<)怎么办?只需要进行转义,对吗?Ruby似乎大多数情况下都没有问题...

%r{(?<!foo\{)}                                                                  
%r[(?<!\[foo)]
%r<\<foo>

就是当你试图在“组选项”中间进行操作时(我猜这里的<!字符被分类为此),后面跟着一个(?时,它就不喜欢了:

%r<(?\<!foo)>
# undefined group option: /(?\<!foo)/

那么如何做到这一点并使Ruby满意?(不更改定界符)

结论

解决方法很简单。我只需要将这个特定的正则表达式更改为使用其他东西,比如%r{...}

但问题仍然存在...

  1. 这里真的没有逃脱<的方法吗?
  2. 是否真的有一些正则表达式是使用特定定界符无法编写的,比如%r<...>
  3. %r<...>是唯一一个具有此问题的正则表达式定界符对吗(在使用它时,有些正则表达式无法编写)。 如果您知道类似于%r{...}/%r[...]的例子,请分享!

版本信息

虽然这种语法可能没有改变过,但并不重要,我的使用情况是:

⟫ ruby -v
ruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-linux]

参考资料:


1
而且,如果你想一想,只有“尖锐”的那些在正则表达式构造中被“不成对”使用。所有其他的配对都是平衡的。我会避开那个<>配对。 - user557597
1
不错,我会试一下 ~ - Tyler Rick
3
很少能遇到一个问题既有趣又具有挑战性,而且表达得非常好。做得好! - Cary Swoveland
期望在正则表达式中平衡使用 <> 是不合理的。我建议向 Ruby 提交错误报告,以便他们修复这个问题。对于 () 等也是一样的,虽然我可以理解为什么他们认为这些只出现平衡的情况,但这并不是一个安全的假设(例如考虑 %r([)]))。 - ՕլՁՅԿ
太棒了,这是一个非常巧妙的 Ruby/正则表达式解析边缘案例。个人而言,我使用 %r(...),因为我觉得它读起来像 MatchData 的“第 0 组”:$~[0]。 - Daniel
显示剩余15条评论
1个回答

2

正如其他人所提到的,这个字符与其他成对边界的字符不同,看起来像是一个疏忽。

至于“真的没有办法在此处转义<吗?”有一种方法……但你可能不会喜欢:

将 < 替换为它的 HTML 实体编码 &lt;。

最初的回答。

%r<(?#{'<'}!foo)> == %r((?<!foo))

使用插值来插入<字符似乎可行。但是,考虑到有更好的选项,除非您打算将正则表达式分成几个部分,否则我会避免使用它... 最初的回答。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接