-4

我有这个正则表达式:

preg_match_all("/<\s*?img\s[^>]*?src=([\"']??)([^\"' >]*?)\1[^>]*?>/si", $content, $m);

这个想法是在一段 HTML 中找到所有图像链接。鉴于此内容:

<p>
    <img alt="" src="/emailimg/interdigital_old.jpg" style="width: 377px; height: 245px; " />Some text here.</p><a href="site.html">test</a>

执行正则表达式后,$m 是一个包含 3 个空数组的数组,但如果我用这个站点测试它,结果是:

Array
(
    [0] => Array
        (
            [0] => <img alt="" src="/emailimg/interdigital_old.jpg" style="width: 377px; height: 245px; " />
        )

    [1] => Array
        (
            [0] => "
        )

    [2] => Array
        (
            [0] => /emailimg/interdigital_old.jpg
        )

)

有什么问题?是配置问题吗?

4

1 回答 1

4

DOM / XPath(即正确)方式:

<?php

  $html = '
<p>
    <img alt="" src="/emailimg/interdigital_old.jpg" style="width: 377px; height: 245px; " />Some text here.</p><a href="site.html">test</a>
';

  $dom = new DOMDocument('1.0');
  $dom->loadHTML($html);

  $xpath = new DOMXPath($dom);

  $links = array();
  foreach ($xpath->query('//img/@src') as $img) $links[] = $img->value;
  print_r($links);

测试和工作

编辑

您的正则表达式不起作用的原因有两个:

  1. 您已经使用双引号字符串声明了您的正则表达式。这通常会导致您意想不到且不完全明显的事情,因为双引号字符串会在传递给 PCRE之前自行插入某些转义序列。这在您的情况下导致的问题\1是被解释为八进制字符定义(如此定义),因此您的表达式中有一个文字0x01(标题开头)字符,而不是\1您希望 PCRE 用作的字符串反向参考。

    我发现当我遇到这样的问题时,一个好的起点是简单echo地筛选表达式以查看 PHP 如何插入您在脚本中声明的字符串。这里是该特定问题的演示。

  2. ([\"']??)- 第二个问号是打破它。我实际上不确定你想用这个来完成什么,它只是一个错误的类型吗?我很难弄清楚 PCRE 是如何解释这一点的,以及它究竟为什么会破坏它,但我只想说它确实如此,并且第二个问号需要去掉。FTR,它的作用是表达式仍然匹配<img>标签,但是下面的捕获组(你真正想要的数据)是空的。

现在让我们分解正则表达式,看看如何改进它:

  • <\s*?img- 这里的非贪婪*是没有意义的,因为\s只匹配空白,下一个序列将是 alpha,<\s*img就足够了。我实际上不确定 HTML 标记是否允许在开头<和标记名称之间有前导空格,但我想允许它不会有任何害处,因为适当的解析器可能会这样做。
  • \s[^>]*?src=(["']??)- 如前所述??,捕获组中的 正在破坏表达式,我不确定您首先要尝试使用它做什么。另外,我再次认为非贪婪*是没有意义的,因为标签将以 结尾>,如果我们最后还没有找到src它,那么它无论如何都不是匹配的。另外,如果我们在不应该出现但解析器可能允许的地方允许空格,我们可能应该在=. 我会将其重写为\s[^>]*src\s*=\s*(["']?).
  • ([^"' >]*?)\1- 假设您担心能够处理未引用的属性,这里没有抱怨。当然,如果您确实知道属性将始终被引用,您可以简单地使用并从前面我们确定使用的引用类型的捕获组中([^\1]*?)\1删除。?
  • [^>]*?>- 这里没有抱怨。
  • /si-修饰符没有意义,因为表达式中的任何地方s都没有s。.它没有任何害处,但也没有帮助,所以它是多余的。

因此,将所有这些放在一起,这就是我编写正则表达式的方式:

/<\s*img\s[^>]*src\s*=\s*(["']?)([^"' >]*?)\1[^>]*>/i

...当转换为带有正确转义引号的 PHP 字符串声明时,如下所示:

$expr = '/<\s*img\s[^>]*src\s*=\s*(["\']?)([^"\' >]*?)\1[^>]*>/i';

...顺便说一句,效果很好

现在,即使考虑到额外的代码,我仍然认为 DOM 方法更好,因为它可能会捕获我的 regex Skillz 忘记的边缘情况。虽然诚然 regex 似乎确实有点快

于 2012-06-08T12:43:07.770 回答