正则表达式有效,但不适用于preg_match_all

时间:2016-08-09 16:08:46

标签: php regex pcre

我无法使用以下正则表达式在PHP中工作。基本上我试图采取一些可怕的Outlook HTML包含编号列表,删除HTML,然后正则表达式纯文本来获取列表。

如果我使用strip_tags()生成的文本并在regex101.com上测试它,它会很好地找到有序列表。如果我在PHP的preg_match_all中使用相同的正则表达式,它会生成一个空数组。

下面的小提琴和regex101:

PHP:

$calendar_code = '
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style>
<!--
@font-face
{font-family:"Cambria Math"}
@font-face
{font-family:Calibri}
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif}
a:link, span.MsoHyperlink
{color:#0563C1;
text-decoration:underline}
a:visited, span.MsoHyperlinkFollowed
{color:#954F72;
text-decoration:underline}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
{margin-top:0cm;
margin-right:0cm;
margin-bottom:0cm;
margin-left:36.0pt;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif}
p.msonormal0, li.msonormal0, div.msonormal0
{margin-right:0cm;
margin-left:0cm;
font-size:12.0pt;
font-family:"Times New Roman",serif}
span.EmailStyle19
{font-family:"Calibri",sans-serif;
color:windowtext}
.MsoChpDefault
{font-size:10.0pt;
font-family:"Calibri",sans-serif}
@page WordSection1
{margin:72.0pt 72.0pt 72.0pt 72.0pt}
div.WordSection1
{}
ol
{margin-bottom:0cm}
ul
{margin-bottom:0cm}
-->
</style>
</head>
<body lang="EN-GB" link="#0563C1" vlink="#954F72">
<div class="WordSection1">
<p class="MsoNormal">This is a test of the agenda and objectives format</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">This shouldn’t get picked up</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Dasdasdasd d asda sd&nbsp; : asd obe: sad neither shood this</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Objective: This is how the object should look, this is a long one</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Agenda:</p>
<p class="MsoListParagraph" style="text-indent:-18.0pt"><span style="">1.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span>Make like a tree</p>
<p class="MsoListParagraph" style="text-indent:-18.0pt"><span style="">2.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span>And</p>
<p class="MsoListParagraph" style="text-indent:-18.0pt"><span style="">3.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span>Get out of here</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Some more stuff here, and here and ::: ;s</p>
<p class="MsoNormal">Sadfdsf sdfdfeswrfew </p>
<p class="MsoNormal">&nbsp;</p>
</div>
</body>
</html>
';

$strip = strip_tags($calendar_code);

echo "<pre>";
echo $strip;

preg_match_all("/^(\d+\.)\s+([^\r\n]+)(?:[\r\n]*)/m", $strip, $matches);
print_r($matches);

PHPFiddle: http://phpfiddle.org/main/code/ygut-5jj5

正如您所看到的,我回应了HTML剥离文本。当我把这个文本放到regex101.com时它完美无缺。 请参见此处: https://regex101.com/r/wW1kC9/1

我认为它可能与行结尾有关,但我在执行\n之前用strip_tags()替换了所有HTML行结尾,但它仍然无法正常工作。

任何人都可以看到为什么这个正则表达式不适用于preg_match_all()

更新

有人指出,不间断的空间是原因,因此在正则表达式中删除或允许它们将修复它。然而,还有人指出,由于这些列表的格式将根据发送列表的电子邮件客户端而非常随机,有些使用<ol>而有些不是例如,正则表达式不适用于所有情况,或者甚至在大多数情况下。

我需要一种更好的方式来获取由任意数量的不同电子邮件客户端创建的列表内容。

对于某些背景,人们会在电子邮件中创建这些列表并将其发送到特殊的电子邮件帐户。然后我的代码访问这些电子邮件并检索列表以供我的应用程序中的其他位置使用。由于这些列表是在许多不同的可用电子邮件客户端中创建的,因此它们总是应用不同的(随机)格式。例如,当您在Outlook 2016中创建列表时,它会添加带有样式的<p><span>标记来创建列表。

3 个答案:

答案 0 :(得分:1)

您必须解码HTML实体:

$strip = html_entity_decode(strip_tags($calendar_code));

然后还有另一个棘手的部分你应该注意:在解码之后,一个不间断的空格将变成它的十六进制表示0xC2 0xA0,它不再与\s令牌匹配所以你有考虑其Unicode代码点00a0

preg_match_all("/^(\d+\.)[\s\x{00a0}]+([^\r\n]+)(?:[\r\n]*)/mu", $strip, $matches);

Live demo

答案 1 :(得分:1)

适用于此"/^(\d+\.)(?:&nbsp;|\s)+([^\r\n]+)(?:[\r\n]*)/m"

显然实体未被删除。

您可以使用此正则表达式删除条带标记后的实体

(?i)[%&](?:[a-z]+|(?:\#(?:[0-9]+|x[0-9a-f]+)));

我会删除它们,解码它们可能会产生不需要的(或未解码的)
字符。

答案 2 :(得分:1)

这是一个替代解决方案,它不使用strip_tags或正则表达式来解析HTML(仅用于解析纯文本),而是使用DOM API。这更加可靠:

function unicodeTrim($str) {
    return preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $str);
}

$doc = new DOMDocument();
$doc->loadHTML($calendar_code);
$xpath = new DOMXpath($doc);
$nodes = $xpath->query('//p[@class="MsoListParagraph"]');
foreach($nodes as $p) {
    // Use the number as array index, and the part after the dot as its value
    $result[intval($p->nodeValue)] = unicodeTrim(explode(".", $p->nodeValue, 2)[1]);
}
print_r($result);

应用于样本数据时的输出:

Array
(
    [1] => Make like a tree
    [2] => And
    [3] => Get out of here
)

eval.in上看到它。