php preg_match排除html标记/属性中的文本以找到正确的位置来剪切字符串

问题描述 投票:0回答:2

我试图确定某些单词在html块内的绝对位置,但前提是它们不在实际的html标签内。例如,如果我想在本文中使用preg_match确定单词“ join”的位置:

<p>There are 14 more days until our <a href="/somepage.html" target="_blank" rel="noreferrer noopener" aria-label="join us">holiday special</a> so come join us!</p>

我可以使用:

preg_match('/join/', $post_content, $matches, PREG_OFFSET_CAPTURE, $offset);

问题是,这与aria-label属性中的单词匹配,当我需要的只是链接后面的单词时。可以在<a></a>之间进行匹配,而不必放在括号内。

我的实际最终目标,除了最后一个元素,我还有大部分(我认为):我正在修剪html块(不是完整的文档)以截断特定的字数。我试图确定最后一个单词结尾的字符,然后将html块的左侧与右侧的html一起加入,以便所有html标记正常关闭。我以为我可以一直工作到遇到一个例子,就像我展示了最后一个单词也在html属性中一样,导致我在错误的位置分割了字符串。到目前为止,这是我的代码:

$post_content = strip_tags ( $p->post_content, "<a><br><p><ul><li>" );
$post_content_stripped = strip_tags ( $p->post_content );
$post_content_stripped = preg_replace("/[^A-Za-z0-9 ]/", ' ', $post_content_stripped);
$post_content_stripped = preg_replace("/\s+/", ' ', $post_content_stripped);
$post_content_stripped_array = explode ( " " , trim($post_content_stripped) );
$excerpt_wordcount = count( $post_content_stripped_array );
$cutpos = 0;
while($excerpt_wordcount>48){
    $thiswordrev = "/" . strrev($post_content_stripped_array[$excerpt_wordcount - 1]) . "/";
    preg_match($thiswordrev, strrev($post_content), $matches, PREG_OFFSET_CAPTURE, $cutpos);
    $cutpos = $matches[0][1] + (strlen($thiswordrev) - 2);
    array_pop($post_content_stripped_array);
    $excerpt_wordcount = count( $post_content_stripped_array );
}
if($pwordcount>$excerpt_wordcount){
    preg_match_all('/<\/?[^>]*>/', substr( $post_content, strlen($post_content) - $cutpos ), $closetags_result);
    $excerpt_closetags = "" . $closetags_result[0][0];
    $post_excerpt = substr( $post_content, 0, strlen($post_content) - $cutpos ) . $excerpt_closetags;
}else{
    $post_excerpt = $post_content;
}

在这种情况下,我实际上是反向搜索字符串,因为我是从字符串末尾逐字逐行地走,所以我知道我的html括号是向后的,例如:

>p/<!su nioj emoc os >a/<laiceps yadiloh>"su nioj"=lebal-aira "renepoon rerreferon"=ler "knalb_"=tegrat "lmth.egapemos/"=ferh a< ruo litnu syad erom 41 era erehT>p<

但是在进行preg_match之前翻转所有括号很容易,或者我认为应该很容易为之准备preg_match帐户。

php regex preg-match
2个回答
1
投票

请勿使用正则表达式解析HTML。

您有一个简单的目标:将文本内容限制为给定的单词数,以确保HTML保持有效。

为此,我建议循环遍历文本节点,直到您计算出一定数量的单词,然后再删除所有内容。

$dom = new DOMDocument();
$dom->loadHTML($post_content);
$xpath = new DOMXPath($dom);
$all_text_nodes = $xpath->query("//text()");
$words_left = 48;
foreach( $all_text_nodes as $text_node) {
    $text = $text_node->textContent;
    $words = explode(" ", $text); // TODO: maybe preg_split on /\s/ to support more whitespace types
    $word_count = count($words);
    if( $word_count < $words_left) {
        $words_left -= $word_count;
        continue;
    }
    // reached the threshold
    $words_that_fit = implode(" ", array_slice($words, 0, $words_left));
    // If the above TODO is implemented, this will need to be adjusted to keep the specific whitespace characters
    $text_node->textContent = $words_that_fit;

    $remove_after = $text_node;
    while( $remove_after->parentNode) {
        while( $remove_after->nextSibling) {
            $remove_after->parentNode->removeChild($remove_after->nextSibling);
        }
        $remove_after = $remove_after->parentNode;
    }
    break;
}
$output = substr($dom->saveHTML($dom->getElementsByTagName("body")->item(0)), strlen("<body>"), -strlen("</body>"));

Live demo


0
投票

好吧,我想出了一种解决方法。我不知道这是否是最优雅的解决方案,所以如果有人看到更好的解决方案,我还是很想听听,但是现在我意识到我不必在要搜索的字符串中实际包含html。要确定切割位置,我只需要长度相同即可。我抓取了所有html元素,并创建了一个虚拟字符串,将它们全部替换为相同数量的星号:

// create faux string with placeholders instead of html for search purposes
preg_match_all('/<\/?[^>]*>/', $post_content, $alltags_result);
$tagcount = count( $alltags_result );
$post_content_dummy = $post_content;
foreach($alltags_result[0] as $thistag){
    $post_content_dummy = str_replace($thistag, str_repeat("*",strlen($thistag)), $post_content_dummy);
}

然后,我只在while循环中使用$post_content_dummy而不是$post_content,以便找到剪切位置,然后使用$post_content进行实际剪切。到目前为止,似乎一切正常。

© www.soinside.com 2019 - 2024. All rights reserved.