Закрыть открытые теги HTML в строке - PullRequest
13 голосов
/ 28 сентября 2010

Ситуация - это строка, которая приводит к чему-то вроде этого:

<p>This is some text and here is a <strong>bold text then the post stop here....</p>

Поскольку функция возвращает тизер (сводку) текста, она останавливается после определенных слов.Где в этом случае тег сильный не закрывается.Но вся строка заключена в абзац.

Можно ли преобразовать приведенный выше результат / вывод в следующее:

<p>This is some text and here is a <strong>bold text then the post stop here....</strong></p>

Я не знаю, с чего начать.Проблема в том, что ... я нашел в сети функцию, которая выполняет регулярные выражения, но она ставит закрывающий тег после строки ... поэтому он не будет проверяться, потому что я хочу, чтобы все теги открывались / закрывались внутри тегов абзаца.Функция, которую я нашел, делает это, что также неправильно:

<p>This is some text and here is a <strong>bold text then the post stop here....</p></strong>

Я хочу знать, что тег может быть сильным, курсивом, чем угодно.Вот почему я не могу добавить функцию и закрыть ее вручную в функции.Любой шаблон, который может сделать это для меня?

Ответы [ 9 ]

32 голосов
/ 28 сентября 2010

Вот функция, которую я использовал ранее, которая работает довольно хорошо:

function closetags($html) {
    preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
    $openedtags = $result[1];
    preg_match_all('#</([a-z]+)>#iU', $html, $result);
    $closedtags = $result[1];
    $len_opened = count($openedtags);
    if (count($closedtags) == $len_opened) {
        return $html;
    }
    $openedtags = array_reverse($openedtags);
    for ($i=0; $i < $len_opened; $i++) {
        if (!in_array($openedtags[$i], $closedtags)) {
            $html .= '</'.$openedtags[$i].'>';
        } else {
            unset($closedtags[array_search($openedtags[$i], $closedtags)]);
        }
    }
    return $html;
} 

Лично я бы сделал это не с помощью регулярных выражений, а с помощью такой библиотеки, как Tidy. Это будет что-то вроде следующего:

$str = '<p>This is some text and here is a <strong>bold text then the post stop here....</p>';
$tidy = new Tidy();
$clean = $tidy->repairString($str, array(
    'output-xml' => true,
    'input-xml' => true
));
echo $clean;
8 голосов
/ 12 мая 2011

Небольшая модификация исходного ответа ... в то время как исходный ответ правильно обрезал теги. Я обнаружил, что во время усечения у меня могут быть нарезанные теги. Например:

This text has some <b>in it</b>

Усечение символа 21 приводит к:

This text has some <

Следующий код основывается на следующем лучшем ответе и исправляет это.

function truncateHTML($html, $length)
{
    $truncatedText = substr($html, $length);
    $pos = strpos($truncatedText, ">");
    if($pos !== false)
    {
        $html = substr($html, 0,$length + $pos + 1);
    }
    else
    {
        $html = substr($html, 0,$length);
    }

    preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
    $openedtags = $result[1];

    preg_match_all('#</([a-z]+)>#iU', $html, $result);
    $closedtags = $result[1];

    $len_opened = count($openedtags);

    if (count($closedtags) == $len_opened)
    {
        return $html;
    }

    $openedtags = array_reverse($openedtags);
    for ($i=0; $i < $len_opened; $i++)
    {
        if (!in_array($openedtags[$i], $closedtags))
        {
            $html .= '</'.$openedtags[$i].'>';
        }
        else
        {
            unset($closedtags[array_search($openedtags[$i], $closedtags)]);
        }
    }


    return $html;
}


$str = "This text has <b>bold</b> in it</b>";
print "Test 1 - Truncate with no tag: " . truncateHTML($str, 5) . "<br>\n";
print "Test 2 - Truncate at start of tag: " . truncateHTML($str, 20) . "<br>\n";
print "Test 3 - Truncate in the middle of a tag: " . truncateHTML($str, 16) . "<br>\n";
print "Test 4: - Truncate with less text: " . truncateHTML($str, 300) . "<br>\n";

Надеюсь, это кому-нибудь поможет.

3 голосов
/ 28 апреля 2017

Этот метод PHP всегда работал для меня.Он закроет все незамкнутые теги HTML.

function closetags($html) {
    preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
    $openedtags = $result[1];

    preg_match_all('#</([a-z]+)>#iU', $html, $result);
    $closedtags = $result[1];
    $len_opened = count($openedtags);
    if (count($closedtags) == $len_opened) {
        return $html;
    }
    $openedtags = array_reverse($openedtags);
    for ($i=0; $i < $len_opened; $i++) {
        if (!in_array($openedtags[$i], $closedtags)){
            $html .= '</'.$openedtags[$i].'>';
        } else {
            unset($closedtags[array_search($openedtags[$i], $closedtags)]);
        }
    }
    return $html;
}
3 голосов
/ 28 сентября 2010

Существует множество других переменных, к которым необходимо обратиться, чтобы получить полное решение, но не охваченных вашим вопросом.

Однако я бы предложил использовать что-то вроде HTML Tidy и, в частности, методы repairFile или repaireString.

1 голос
/ 16 апреля 2018

А как насчет использования собственного класса PHP DOMDocument?Он по сути анализирует HTML и исправляет синтаксические ошибки ... Например:

$fragment = "<article><h3>Title</h3><p>Unclosed";
$doc = new DOMDocument();
$doc->loadHTML($fragment);
$correctFragment = $doc->getElementsByTagName('body')->item(0)->C14N();
echo $correctFragment;

Однако у этого подхода есть несколько недостатков.Во-первых, он оборачивает оригинальный фрагмент в тег <body>.Вы можете легко избавиться от него с помощью чего-то вроде (preg_) replace () или заменив функцию ...->C14N() какой-то пользовательской функцией innerHTML (), как это предлагается, например, в http://php.net/manual/en/book.dom.php#89718. Второй недостаток заключается в том, что PHP бросаетпредупреждение «недопустимый тег в сущности», если используются HTML5 или пользовательские теги (тем не менее, он все равно будет работать правильно).

0 голосов
/ 11 июня 2019

Это работает для меня, чтобы закрыть все открытые теги HTML в скрипте.

<?php
function closetags($html) {
preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
$openedtags = $result[1];
preg_match_all('#</([a-z]+)>#iU', $html, $result);
$closedtags = $result[1];
$len_opened = count($openedtags);
if (count($closedtags) == $len_opened) {
    return $html;
}
$openedtags = array_reverse($openedtags);
for ($i=0; $i < $len_opened; $i++) {
    if (!in_array($openedtags[$i], $closedtags)) {
        $html .= '</'.$openedtags[$i].'>';
    } else {
        unset($closedtags[array_search($openedtags[$i], $closedtags)]);
    }
}
return $html;
}
0 голосов
/ 08 ноября 2017

если установлен модуль tidy, используйте расширение php tidy:

tidy_repair_string($html)

ссылка

0 голосов
/ 09 января 2017

Я сделал этот код, который выполняет эту работу совершенно правильно ...

Это старая школа, но эффективная, и я добавил флаг для удаления незаконченных тегов, таких как "бла-бла http://stackoverfl"

public function getOpennedTags(&$string, $removeInclompleteTagEndTagIfExists = true) {

    $tags = array();
    $tagOpened = false;
    $tagName = '';
    $tagNameLogged = false;
    $closingTag = false;

    foreach (str_split($string) as $c) {
        if ($tagOpened && $c == '>') {
            $tagOpened = false;
            if ($closingTag) {
                array_pop($tags);
                $closingTag = false;
                $tagName = '';
            }
            if ($tagName) {
                array_push($tags, $tagName);
            }
        }
        if ($tagOpened && $c == ' ') {
            $tagNameLogged = true;
        }
        if ($tagOpened && $c == '/') {
            if ($tagName) {
                //orphan tag
                $tagOpened = false;
                $tagName = '';
            } else {
                //closingTag
                $closingTag = true;
            }
        }
        if ($tagOpened && !$tagNameLogged) {
            $tagName .= $c;
        }
        if (!$tagOpened && $c == '<') {
            $tagNameLogged = false;
            $tagName = '';
            $tagOpened = true;
            $closingTag = false;
        }
    }

    if ($removeInclompleteTagEndTagIfExists && $tagOpened) {
        // an tag has been cut for exemaple ' blabh blah <a href="sdfoefzofk' so closing the tag will not help...
        // let's remove this ugly piece of tag
        $pos = strrpos($string, '<');
        $string = substr($string, 0, $pos);
    }

    return $tags;
}

Пример использования:

$tagsToClose = $stringHelper->getOpennedTags($val);
$tagsToClose = array_reverse($tagsToClose);

foreach ($tagsToClose as $tag) {
    $val .= "</$tag>";
}
0 голосов
/ 28 сентября 2010

Использование регулярного выражения не идеальный подход для этого.Вместо этого вы должны использовать html-анализатор для создания действительной объектной модели документа.

В качестве второго варианта, в зависимости от того, что вы хотите, вы можете использовать регулярное выражение для удаления любых и всех тегов html из вашей строки перед тем, как поместитьэто в теге <p>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...