【问题标题】:Regex and php question, need non-greedy search!正则表达式和 php 问题,需要非贪婪搜索!
【发布时间】:2011-03-21 19:54:59
【问题描述】:

我在尝试编写非贪婪的正则表达式语句时遇到问题。

这是我的字符串:

<strong>name</strong><strong>address</strong>mailto:blabla@email.com

这是我的正则表达式查询:

<strong>(.*?)</strong>.*?([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})

问题是我需要地址,而不是字符串中的名称。所以我需要正则表达式查询是非贪婪的,并采用最近的&lt;strong&gt;&lt;/strong&gt; 而不是最远的。

在我的搜索字符串中也有多个 this 实例,所以它必须一次匹配多个实例,而不是在它前面添加一个 .*(贪婪)。

所以它必须匹配这个的所有实例,并提取地址,而不是名称:

   <strong>name</strong><strong>address1</strong>mailto:blabla@email.com
   <strong>name</strong><strong>address2</strong>mailto:blabla@email.com
   <strong>name</strong><strong>address3</strong>mailto:blabla@email.com
   <strong>name</strong><strong>address4</strong>mailto:blabla@email.com

提前致谢!

【问题讨论】:

  • HTML+RegEx=Sh*t-storm 正在酝酿中。准备“不要使用正则表达式”答案/cmets。 -- 另一方面,我不确定我是否 100% 理解这个问题。您也可以提供示例捕获吗? (也许在您的正则表达式末尾添加$ 是您正在寻找的?)
  • 是的,我只是在寻找关于那个布拉德主题的好帖子,哈哈。雪人,建议你使用 phpquery code.google.com/p/phpquery,因为它可以像 jquery 一样遍历 DOM。
  • @Jason:PHP 已经有了DOMDocument。 ;-)
  • @Brad,“phpquery”库提供了许多普通 DOM 中缺少的选择器快捷方式。
  • 我认为尝试的解决方案的问题在于它尝试“反向”应用非贪婪匹配,从而迫使&lt;strong&gt;/&lt;/strong&gt;pair 匹配到最右边可以在邮件地址之前。但这不是非贪婪匹配的工作方式。对于快速而肮脏的解决方案,我只使用[^&lt;]* 而不是.*? - 因为&lt; 在HTML 中是非法的,除了开始一个标签,它将匹配地址字段的任何合法内容,但是防止它与标签匹配。

标签: php regex regex-greedy


【解决方案1】:

首先,正则表达式是匹配 HTML 的次优工具(这是一个很好的例子)。如果您知道如何使用解析器,您会更满意(也许一位 PHP 大师可以推荐一个)。

话虽如此,使用正则表达式的更好方法可能是明确匹配(并丢弃)第一个 &lt;strong&gt; 标记:

<strong>.*?</strong><strong>(.*?)</strong>.*?([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})

这绝不是一个好的、可靠的、防弹的解决方案,但至少它适用于您的示例数据。

或者,如果您可以更具体地说明相关标签之间/之后允许的内容,如何:

<strong>([^<>]*)</strong>(?:mailto:)?([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})

【讨论】:

  • 实际问题是电子邮件捕获之前的.*? 也捕获了实际需要的&lt;strong&gt;。 (尽管我会为 OP 提及它,以便他们知道哪里出错了)。
【解决方案2】:

查看您的测试数据,我推断出以下规则:如果...

  1. 姓名和地址都包含在 STRONG 元素中,电子邮件紧随其后,并且
  2. STRONG 元素的属性、名称和地址都没有尖括号,并且
  3. 电子邮件地址部分始终以 mailto: 开头,并且
  4. 两个 STRONG 元素中没有其他 HTML 元素,

那么这个经过测试的代码应该可以解决问题:

$re = '%
    # Capture name and address in <strong> element then email.
    <strong[^>]*>\s*([^<>]+)</strong\s*>\s*  # $1: Name.
    <strong[^>]*>\s*([^<>]+)</strong\s*>\s*  # $2: Address.
    (mailto:\S+)                             # $3: Email.
    %ix';
$count = preg_match_all($re, $text, $matches);
if ($count) {
    printf("%d matches found:\n", $count);
    print_r($matches);
    for ($i = 0; $i < $count; ++$i) {
        printf("Match %d: Name: \"%s\", Address: \"%s\", Email: \"%s\":\n",
            $i + 1, $matches[1][$i], $matches[2][$i], $matches[3][$i]);
    }
} else {
    printf("No matches found.\n");
}

【讨论】:

    【解决方案3】:

    不要使用正则表达式来解析 HTML。

    http://htmlparsing.com/php.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-29
      • 2011-04-27
      相关资源
      最近更新 更多