【问题标题】:Negating a backreference in Regular Expressions在正则表达式中否定反向引用
【发布时间】:2011-12-24 17:43:36
【问题描述】:

如果字符串具有这种预测格式:

value = "hello and good morning"

"(引号)也可能是 '(单引号),并且结束字符(' 或 ")将与开始字符相同。我想匹配引号之间的字符串。

\bvalue\s*=\s*(["'])([^\1]*)\1

(两个 \s 允许 = 符号附近的任何空格)

第一个“捕获的组”(在第一对括号内)- 应该与应该是 ' 或 " 的开头引号匹配 然后 - 我应该允许任何数量的字符不是第一组中捕获的字符,然后我希望在组中捕获的字符(括起来的引号)。

(应在第二个捕获组中捕获所需的字符串)。
但这不起作用。

这样做:

\bvalue\s*=\s*(['"])([^"']*)["']

但我想确保左引号和右引号(双引号或单引号)相同。


编辑
目标基本上是获取一个锚点的开始标签,该锚点在其类属性中包含某个类名,我想涵盖包含 (') 或 (") 的类属性的罕见情况。

按照这里的所有建议,我使用了该模式:

<\s*\ba\b[^<>]+\bclass\s*=\s*("|'|\\"|\\')(?:(?!\1).)*\s*classname\s*(?:(?!\1).)*\1[^>]*>

含义
找到一个标签打开的标志。
允许任何空格。
找到单词
允许任何非结束标签。
查找“类(任何空格)=(任何空格)”
获取开头引号,以下之一:(" or ' or \" or \')。
来自 Alan Moore 的回答:允许任何不是开头引号的字符。
查找类名
允许任何不是开头引号的字符。
找到与开头相同的结尾引号。
允许任何未关闭标签字符。
找到结束标记 char。

【问题讨论】:

  • 引号可以转义吗? IE。 value = 'O\'Neill'

标签: regex backreference regex-group negate


【解决方案1】:

您必须使用否定前瞻,而不是否定字符类:

\bvalue\s*=\s*(["'])(?:(?!\1).)*\1

(?:(?!\1).)* 一次消耗一个字符, 前瞻已确认该字符不是捕获组 (["'']) 匹配的任何字符。一个字符类,无论是否否定,一次只能匹配一个字符。就正则表达式引擎所知,\1 可以表示任意数量的字符,在这种情况下,无法说服\1 将只包含"'。因此,您必须使用更通用(且可读性更低)的解决方案。

【讨论】:

  • 所以,这部分: (?!\1) 表示:匹配后面的任何内容,但确保它不是 \1 中的内容?这是我需要的,谢谢。
  • 我看到它只有在非捕获组中才能正常工作,就像你一样。我只明白为什么它必须在非捕获组内......
  • 否定的前瞻,(?!\1),实际上并不匹配任何东西,它只是断言它不可能在当前位置匹配\1。实际上匹配(即消耗)下一个字符的是.
  • 至于非捕获组,那只是策略;我使用它是因为我没有必须在那里使用捕获组。我发布的正则表达式应该可以使用任何一种方式,尽管((?!\1).)* 会无缘无故地低效。更重要的是,组根据它们在正则表达式中的位置进行编号,因此尽可能使用非捕获组可以更轻松地跟踪捕获组编号。
  • 你...必须使用负前瞻 (?)
【解决方案2】:

你可以使用:

\bvalue\s*=\s*(['"])(.*?)\1

See it

【讨论】:

  • @YuvalA:你是对的。 char 类中不能有反向引用。
  • 我强烈建议您删除这个答案——这是一种看起来应该可行的不幸案例,也是最直接的解决方案,但实际上并不可行。
【解决方案3】:

在不知道您需要哪些信息的情况下(或者甚至不知道您使用此正则表达式的语言或工具),我可以建议许多路径。

使用这些字符串:

value = "hello and good morning"
value = 'hola y buenos dias'
value = 'how can I say "goodbye" so soon?'
value = 'why didn\'t you say "hello" to me this morning?'
value = "Goodbye! Please don't forget to write!"
value = 'Goodbye! Please don\'t forget to write!'

这个表达式:

"((\\"|[^"])*)"|'((\\'|[^'])*)'

将匹配这些字符串:

"hello and good morning"
'hola y buenos dias'
'how can I say "goodbye" so soon?'
'why didn\'t you say "hello" to me this morning?'
"Goodbye! Please don't forget to write!"
'Goodbye! Please don\'t forget to write!'

当使用单个前面的\ 进行转义时,它将允许“其他”类型的引用或相同类型的引用。引用字符串的内容在第 1 组或第 3 组中。您可以通过获取第一个(或最后一个)字符来确定使用了哪种类型的引号。

如果您需要将其中一些内容放在特定的匹配组中,请给出更具体的示例(并包括不应该工作但看起来可能很接近的内容)

请询问您是否愿意走这条路并需要更多帮助

【讨论】:

  • 我很好奇反对票的来源 - 有人想告诉我我错过了什么吗?我很高兴每个人都喜欢更现代的正则表达式的更时髦、更少“常规”功能而不是简单的交替,但是……我的方法……对某些用例不起作用吗??
  • 我不能代表反对者,但是:'this shouldn\'t match
【解决方案4】:

回答这个问题How to use a numerical reference in neglected set?

这里是因为它被标记为与这个完全相同。

无法真正在类中指定捕获组。
可以做的是在否定断言中指定字符,像这样

(["'])((?:(?!\1)[\S\s])*)(\1)

展开

 ( ["'] )                      # (1)
 (                             # (2 start)
      (?:
           (?! \1 )
           [\S\s] 
      )*
 )                             # (2 end)
 ( \1 )                        # (3)

请注意,在原始帖子中 [^char] 通常匹配换行符
同样,但由于这是 JavaScript(旧 JS),因此不能使用点。
请改用[\S\s],它匹配任何字符。

【讨论】:

    【解决方案5】:

    我在寻找与我的模式匹配的帮助时遇到了这篇文章:

    value="long text with \"quoted values\" and more"

    Alan Moore 当前的最佳答案在这里非常好,但不考虑引用的转义。因此,感谢 Alan,您可以在允许使用 \ 转义引用时使用此模式:

    \bvalue\s*=\s*(["'])(?:(?!(?<!\\)\1).)*\1
    

    奖金信息

    也许您在这里寻找的模式与我的目的相同,因此我也会分享我的最终解决方案。我必须匹配几个键值对,其格式与通常在节点中列出的 html 属性相同,例如:one="first" two="second"

    以下正则表达式将与此匹配,并将捕获组命名为 keyvalue

    \b(?P<key>[^=\s]*)\s*=\s*(["'])(?P<value>(?:(?!(?<!\\)\2).)*)\2
    

    【讨论】:

      【解决方案6】:

      当我们为 CMS Effcore 编写 Markdown 解析器时,我们尝试了不同的变体以确保尽可能高的速度。这些变体如下所示:

      替换示例:

      "markdown *text*"
      

      到:

      "markdown <em>text</em>"
      

      字符“*”和“_”的 PHP 代码 #1(贪婪模式):

      preg_replace('%'.'([*_])'.'(?<phrase>.+?)'.'\\1'.'%sS', '<em>$2<em>', $text);
      

      字符“*”和“_”的 PHP 代码 #2(反向引用中的否定):

      preg_replace('%'.'([*_])'.'(?<phrase>(?:(?!\\1).){1,})'.'\\1'.'%sS', '<em>$2<em>', $text);
      

      单字符“*”的 PHP 代码 #3(字符类中的否定):

      preg_replace('%'.'([*])'.'(?<phrase>[^*]{1,})'.'[*]'.'%sS', '<em>$2<em>', $text);
      

      案例#1(“贪婪模式”)比案例#2(“反向引用中的否定”)更快。

      经过 1000000 次迭代测试:

      1. 0.0245740413665 秒。
      2. 3.3793921470642 秒。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-28
        • 2018-07-13
        • 1970-01-01
        相关资源
        最近更新 更多