【问题标题】:Why this regex is not greedy in PHP为什么这个正则表达式在 PHP 中不贪心
【发布时间】:2014-02-25 21:17:06
【问题描述】:

这个正则表达式应该匹配列表,就像在 Markdown 中一样:

/((?:(?:(?:^[\+\*\-] )(?:[^\r\n]+))(?:\r|\n?))+)/m

它在 Javascript 中工作(添加了g 标志),但我在将它移植到 PHP 时遇到了问题。它的行为不贪婪。这是我的示例代码:

$string = preg_replace_callback('`((?:(?:(?:^\* )(?:[^\r\n]+))(?:\r|\n?))+)`m', array(&$this, 'bullet_list'), $string);

function bullet_list($matches) { var_dump($matches) }

当我输入一个包含三行的列表时,它会显示:

array(2) { [0]=> string(6) "* one " [1]=> string(6) "* one " } array(2) { [0]=> string(6) "* two " [1]=> string(6) "* two " } array(2) { [0]=> string(8) "* three " [1]=> string(8) "* three " } 

显然var_dump 被调用了 3 次,而不是像我期望的那样只调用一次,因为正则表达式是贪婪的,并且必须匹配尽可能多的行。我已经在 regex101.com 上对其进行了测试。 如何让它正常工作?

【问题讨论】:

  • 那些反引号真的存在吗?
  • @TimPietzcker 反引号作为正则表达式修饰符
  • @Tim Pietzcker 已修复。当我不需要时使用反斜杠来逃避反引号
  • 啊,我曾将反斜杠视为斜杠并感到困惑:)
  • 您需要小心,因为您的匹配仅在文本以项目符号列表开头时才有效。它也只匹配一个列表。如果您想匹配所有列表,请参阅我的答案。

标签: php regex preg-replace-callback


【解决方案1】:

您的正则表达式可以简化为:

(?:^[+*-] [^\r\n]+\R*)+

没有必要做所有这些组。
\R 表示任何类型的换行符\n\r\r\n

编辑: \R 在字符类中失去了它的特殊含义。 [\R] 表示 R
感谢 HamZa

【讨论】:

  • 我不知道\R 快捷方式,太好了!
  • 这不也匹配后续的非列表行吗?
  • @TimPietzcker:是的,缩短正则表达式非常有用。请参阅en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions 处的 § 反斜杠-R 选项
  • @M42 不幸的是,\R 在字符类中不起作用。 [\R]will match R literally.
  • @HamZa:是的,你是对的。太糟糕了......答案已相应编辑。
【解决方案2】:

如果您的输入文本中有 \r\n 换行符,此正则表达式将无法正常工作。

(?:\r|\n?) 部分匹配 either an \r an \n,但不能同时匹配两者。 (regex101 仅将换行符视为\n,因此它在那里工作)。

以下是否有效?

/(?:(?:(?:^[+*-] )(?:[^\r\n]+))[\r\n]*)+/m

(或者,在删除所有不必要的非捕获组之后 - 谢谢@M42!)

/(?:^[+*-] [^\r\n]+[\r\n]*)+/m

【讨论】:

  • 是的,它有效,但您忘记将整个表达式括在括号中:/((?:(?:(?:^[+*-] )(?:[^\r\n]+))[\r\n]*)+)/m
  • @Juribiyan:不需要外括号
【解决方案3】:

这将匹配所有带项目符号的行,直到它到达没有项目符号的第一行。

(?<=^|\R)\*[\s\S]+?(?=$|\R[^*])
  • \* 匹配一个项目符号,其中:
    • (?&lt;=^|\R) 前面是字符串的开头或换行符。
  • [\s|S]+? 非贪婪匹配任何字符
    • (?=$|\R[^*]) 匹配的序列后跟字符串结尾或换行符后跟 *。本质上,这意味着当找到非项目符号行或字符串结尾时,序列匹配就完成了。

结果:

结果匹配显示在下面的 RegexBuddy 输出中(Regex 101 无法处理):

【讨论】:

  • See my previous comment about \R 在一个字符类中。
  • @HamZa 非常感谢,这总是让我绊倒。需要记住这是一个位置匹配。
猜你喜欢
  • 2012-04-27
  • 1970-01-01
  • 1970-01-01
  • 2012-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-12
相关资源
最近更新 更多