为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

PHP
一只萌萌小番薯 2023-04-28 16:38:05
我试图确定某些单词在 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;}但是在执行 preg_match 之前翻转所有括号很容易,或者我假设应该很容易让 preg_match 考虑到这一点。
查看完整描述

2 回答

?
慕容3067478

TA贡献1773条经验 获得超3个赞

不要使用正则表达式来解析 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>"));


查看完整回答
反对 回复 2023-04-28
?
喵喵时光机

TA贡献1846条经验 获得超7个赞

好的,我想出了一个解决方法。我不知道这是否是最优雅的解决方案,所以如果有人看到更好的解决方案,我仍然很想听听,但现在我意识到我不必在我正在搜索的字符串中实际包含 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);

}

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


查看完整回答
反对 回复 2023-04-28
  • 2 回答
  • 0 关注
  • 133 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信