【问题标题】:PHP preg_match group repetitionPHP preg_match 组重复
【发布时间】:2011-03-29 01:30:14
【问题描述】:

我有一个(php5.2 和 5.3)正则表达式,它需要从用户帖子(可能包括电子邮件地址和超链接)中提取前 $x 个句子,并且在试图找出原因时遇到了麻烦(是的,它是一个丑陋的正则表达式;我会在它工作时对其进行优化):

/^(([^.!?]+|(\w+[.@?&=%:])+\w+)+[.!?]+\s){0,4}/

返回前四个句子,但是

/^(([^.!?]+|(\w+[.@?&=%:])+\w+)+[.!?]+\s){0,5}/

不返回任何匹配项。我的理解是 {0,5} 应该匹配前一个组 0 到 5 次,因此如果它只能匹配它 4 次,它应该仍然有效。

谁能解释一下这种行为?

更新:$x 只是一个任意数字;在正则表达式中使用 {0,$x}。帖子被过滤为由单个空格分隔的句子。对不起,丑陋的表情......现在已经研究了几天,它让我很头疼......做出了sawa建议的改变。我的主要问题是关于行为,组匹配的内容应该没有那么重要。

Update2:这基本上就是我正在做的事情:

function extractSummary($message, $limit) {
  $expr = '/^(([^.!?]+|(\w+[.@?&=%:])+\w+)+[.!?]+\s){0,'.$limit.'}/';
  $msg = preg_replace('/[\x00-\x1f\x80-\xff]/', "\n" strip_tags($message));
  $msg = trim(preg_replace('/(\n|\s| )+/', ' ', $msg)).' ';
  preg_match($expr, $msg, $summary);
  return $summary[0];
}

一个句子(至少在我看来,没有进入 NLP 领域,因为它只是用于站点中的一个功能)是任何不超过句点、感叹号或问号的东西,但句点可以出现在句子中的URL 的电子邮件地址。这个正则表达式的最后一个版本最多只计算了 5 个句点,因此在链接和电子邮件地址上中断了。

更新 3:意识到我刚刚添加了更可怕的代码,我将解释最后一个。一些发布的内容被发现有非打印字符(如 \r 等)与正则表达式不能很好地配合,所以我用第一个 preg_replace 删除了非打印字符。第二个用一个空格替换任何进一步的空白组,因此希望句子之间正好用一个空格分隔。

【问题讨论】:

  • 什么是 $x 句?帖子怎么样?
  • 在此之前,您应该清理您的正则表达式。例如,您不需要((\w+[....\w) 中最外面的一对括号,并且您在捕获方面不一致:有时您有(...),而有时您有(?:...)。仅当您要提取该部分时才使用前者。我认为仅仅发布一个复杂的正则表达式并让人们遵循它是不礼貌的。
  • 感谢您的建议,但我认为一些例句是必要的,或者至少您应该告诉我们您的想法。一个句子只是一个句号的序列吗?从你所拥有的推测,情况可能并非如此。只有当它是电子邮件地址的一部分时,才允许在句子中使用句点?定义句子的条件是什么?

标签: php regex preg-match


【解决方案1】:

我认识的句子如下:

一句话是:

  • 最短的序列,最长为句号、感叹号或问号,
  • 可选地后跟单引号或双引号,
  • 后跟空格或字符串结尾。

空格或字符串结尾的这种要求会处理电子邮件地址中的句点,因为电子邮件地址中的句点不会出现在空格之前或字符串末尾。

/[^ ](?:.*?[.!?]['"]*(?= |\z)){0,4}/

【讨论】:

  • 这会导致不匹配,即使在基本情况下也是如此。尝试使用“这是一个句子。这是第 2 个句子。这个句子有一个 email.address@domain.com。这个有一个链接 somewhere.com。这个句子不应该出现在输出中。”
  • 是的,都试过了。之前更新,结果不是不匹配,而是匹配是一个空字符串。从正则表达式来看这是有道理的,但不是我所追求的——我需要提取句子,而不是测试匹配。
  • 只提取第一句话。稍作改动后,它会得到前 4 个句子:“/(.*?[.!?](?=\s)){0,4}/”,但是在遇到电子邮件地址或链接时仍然会中断。
  • 不处理以引号结尾的句子,例如Like "this one". 或 He said: 'it didn't work!'
  • 我在哪里添加引号是错误的。我修复了它,并编辑了整个答案。
【解决方案2】:

正则表达式以无条件匹配空白字符结束。如果输入中恰好有 5 个句子并且在最后一个句点之后没有空格,则第一个将匹配,但第二个不会。

【讨论】:

  • 这并不能解释为什么它在第一个正则表达式中成功而在第二个正则表达式中失败,正如 Rodney 所说。
  • 帖子在通过正则表达式之前在末尾插入了一个空格,以避免这种可能性。
【解决方案3】:

这个经过测试的函数应该可以解决问题:

function get_sentences($text, $x) {
    $regex = "/\A(?:.*?[\w\"'][.?!](?=['\"]?\s|\$)){0,{$x}}/ms";
    if (preg_match($regex, $text, $matches)) return $matches[0];
    return ''; // Never get here (will always match).
}

这是正则表达式的注释版本:

$regex = '/# Match first $x sentences, each ending in [.?!]
    \A                # Anchor to beginning of string
    (?:               # Non-capture group to apply count
      .*?             # Lazily match zero or more characters.
      [\w"\']         # Last char before end is word or quote.
      [.?!]           # End of sentence puntuation [.?!]
      (?=[\'"]?\s|$)  # But only if followed by space or EOL
    ){0,5}            # Match from zero to $x sentences.
    /smx';

请注意,这也处理以引号结尾的句子,例如"This one." 或 "This one!" 或“这个”?

【讨论】:

  • 很好,但在带有链接的句子之前停止输出:例如“这是第一个句子。这是第二个。这是某人@somewhere.com 第三个。”只输出“这是第一句。这是第二句。”即使 $x > 2。
  • @Rodney:不。它适用于您的示例,结束标点符号必须后跟空格或行尾。
  • 我的错,你的表情打错了。效果很好。谢谢大家!
猜你喜欢
  • 1970-01-01
  • 2013-11-29
  • 2014-07-06
  • 2020-10-16
  • 2015-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-20
相关资源
最近更新 更多