我有这个正则表达式:
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
)
)
怎么了?是配置问题吗?
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);
经过测试并正常工作。
编辑
您的正则表达式不起作用的原因有两个:
您已使用双引号字符串声明了正则表达式。这通常会导致您意想不到的事情,并且并不完全明显,因为双引号字符串会在传递给 PCRE 之前插入某些转义序列本身。在您的情况下,这造成的问题是
'1
被解释为八进制字符定义(如此处定义),因此您的表达式中包含文字0x01
(标题开头)字符,而不是您希望 PCRE 用作反向引用的'1
字符串。
我发现当我遇到这样的问题时,一个好的起点是简单地echo
表达式进行筛选,以查看 PHP 如何插入您在脚本中声明的字符串。这是该特定问题的演示。(['"']??)
- 第二个问号正在打破它。我实际上不确定你想用这个完成什么,它只是一个错误的类型吗?我很难弄清楚PCRE是如何解释这一点的,以及它为什么会破坏它,但只要说它确实如此就足够了,第二个问号需要去掉。FTR,它的效果是表达式仍然与<img>
标记匹配,但以下捕获组(您实际需要的数据)为空。
现在让我们分解正则表达式,看看如何改进它:
-
<'s*?img
- 这里的非贪婪*
毫无意义,因为's
只匹配空格,下一个序列将是 alpha,只需<'s*img
就足够了。我实际上不确定 HTML 标签是否允许在开头<
和标签名称之间有前导空格,但我想允许它不会有任何伤害,因为适当的解析器可能会。 -
's[^>]*?src=(["']??)
- 如前所述,捕获组中的??
正在破坏表达式,我不确定您首先要用它做什么。另外,我再次认为非贪婪*
毫无意义,因为标签将以>
结尾,如果我们到最后还没有找到src
,那么无论如何它都不是匹配的。另外,如果我们在不应该的地方允许空格,但解析器可能会允许,我们可能应该在=
周围允许它。我会把它重写成's[^>]*src's*='s*(["']?)
. -
([^"' >]*?)'1
- 假设您担心能够处理未引用的属性,这里没有抱怨。当然,如果您知道属性将始终被引号,则只需使用([^'1]*?)'1
并从前面的捕获组中删除?
,我们在其中确定了正在使用的引用类型。 -
[^>]*?>
- 这里没有抱怨。 -
/si
-s
修饰符毫无意义,因为表达式中的任何地方都没有.
。它不会造成任何伤害,但也无济于事,所以它是多余的。
所以,把所有这些放在一起,这就是我写正则表达式的方式:
/<'s*img's[^>]*src's*='s*(["']?)([^"' >]*?)'1[^>]*>/i
。当转换为带有引号正确转义的 PHP 字符串声明时,如下所示:
$expr = '/<'s*img's[^>]*src's*='s*(["'']?)([^"'' >]*?)'1[^>]*>/i';
。顺便说一下,这很好用。
现在,我仍然认为即使考虑额外的代码,DOM 方法也更好,因为它可能会捕获我的正则表达式技能忘记的边缘情况。尽管不可否认,正则表达式似乎确实更快一些。