【问题标题】:How to get the string position of the middle-most element within HTML content?如何获取 HTML 内容中最中间元素的字符串位置?
【发布时间】:2020-08-20 13:20:42
【问题描述】:

我正在处理来自所见即所得编辑器的 HTML 格式的新闻文章,我需要找到它的中间部分,但在视觉/HTML 上下文中,这意味着两个根元素之间的空白位置。如果您想将文章分成两页,比方说,在可能的情况下,每页的段落数相同。

所有根元素似乎都以段落的形式出现,这很容易计算,很简单

$p_count = substr_count($article_text, '<p');

返回开头段落标签的总数,然后我可以查找($p_count/2)-第一个段落出现的strpos

但问题在于嵌入的推文,其中包含段落,有时出现在blockquote &gt; p 下,有时显示为center &gt; blockquote &gt; p

所以我转向 DOMDocument。这个小 sn-p 给了我中间的第 n 个元素(即使元素是 div 而不是段落,这很酷):

$dom = new DOMDocument();
$dom->loadHTML($article_text);
$body = $dom->getElementsByTagName('body');
$rootNodes = $body->item(0)->childNodes;

$empty_nodes = 0;
foreach($rootNodes as $node) {
    if($node->nodeType === XML_TEXT_NODE && strlen(trim($node->nodeValue)) === 0) {
        $empty_nodes++;
    }
}

$total_elements = $rootNodes->length - $empty_nodes;
$middle_element = floor($total_elements / 2);

但是我现在如何在我的原始 HTML 源中找到这个中间元素的字符串偏移量,以便我可以指向文章文本字符串中的这个中间位置?特别是考虑到 DOMDocument 将我给它的 HTML 转换成一个完整的 HTML 页面(带有 doctype、head 等等),所以它的输出 HTML 比我原来的 HTML 文章源要大。

【问题讨论】:

  • 找到正确的字符串偏移量并不容易,我认为你最好在 DOM 中你确定的位置插入一个新节点。 “所以它的输出 HTML 比我原来的 HTML 文章源要大” - DOMDocument 的save 方法允许您再次指定要“导出”的节点,所以如果您指定正文节点在那里,你不应该有太多多余的数据,并且可能可以使用字符串函数从结果中删除 &lt;body&gt;&lt;/body&gt;
  • @CBroe 可能是一个很好的解决方法,我会尝试使用它,感谢您的建议

标签: php domdocument


【解决方案1】:

好的,我解决了。

我所做的是匹配文章中的所有 HTML 标记,使用 preg_match_allPREG_OFFSET_CAPTURE 标志,它会记住模式匹配的字符偏移量。然后我依次遍历所有这些,并计算我所处的深度;如果它是一个开始标签,我计算深度+1,并计算一个结束-1(注意自动关闭标签)。每次在结束标记后深度变为零时,我都将其视为又关闭了一个根元素。如果最后我到达深度0,我认为我计算正确。

现在,我可以取我计算的根元素的数量,除以 2 得到中间的一个(奇数为 +-1),然后查看该索引处元素的偏移量,如preg_match_all以前。

如果有人需要做同样的事情,完整的代码如下。

如果is_self_closing() 函数是使用正则表达式编写的,然后检查in_array($self_closing_tags),而不是foreach 循环,它可能会加快速度,但在我的情况下,它对我来说没有足够的区别麻烦。

function calculate_middle_of_article(string $text, bool $debug=false): ?int {
    
    function is_self_closing(string $input, array $self_closing_tags): bool {
        foreach($self_closing_tags as $tag) {
            if(substr($input, 1, strlen($tag)) === $tag) {
                return true;
            }
        }
        return false;
    }

    $self_closing_tags = [
        '!--',
        'area',
        'base',
        'br',
        'col',
        'embed',
        'hr',
        'img',
        'input',
        'link',
        'meta',
        'param',
        'source',
        'track',
        'wbr',
        'command',
        'keygen',
        'menuitem',
    ];

    $regex = '/<("[^"]*"|\'[^\']*\'|[^\'">])*>/';
    preg_match_all($regex, $text, $matches, PREG_OFFSET_CAPTURE);
    $debug && print count($matches[0]) . " tags found   \n";

    $root_elements = [];
    $depth = 0;

    foreach($matches[0] as $match) {
        if(!is_self_closing($match[0], $self_closing_tags)) {
            $depth+= (substr($match[0], 1, 1) === '/') ? -1 : 1;
        }
        $debug && print "level {$depth} after tag: " . htmlentities($match[0]) . "\n";

        if($depth === 0) {
            $root_elements[]= $match;
        }
    }

    $ok = ($depth === 0);
    $debug && print ($ok ? 'ok' : 'not ok') . "\n";

    // has to end at depth zero to confirm counting is correct
    if(!$ok) {
        return null;
    }

    $debug && print count($root_elements) . " root elements\n";

    $element_index_at_middle = floor(count($root_elements)/2);
    $half_char = $root_elements[$element_index_at_middle][1];
    $debug && print "which makes the half the {$half_char}th character at the {$element_index_at_middle}th element\n";

    return $half_char;
}

【讨论】:

    猜你喜欢
    • 2019-01-28
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多