Закрыть открытые HTML-теги в строке
Ситуация - это строка, которая приводит к чему-то вроде этого:
<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>
Я не знаю, с чего начать. Проблема в том, что.. Я нашел функцию в Интернете, которая делает это регулярное выражение, но она помещает закрывающий тег после строки.. поэтому он не будет проверяться, потому что я хочу, чтобы все теги open/close в тегах абзаца. Функция, которую я нашел, делает это также неверно:
<p>This is some text and here is a <strong>bold text then the post stop here....</p></strong>
Я хочу знать, что тег может быть сильным, курсивом, чем угодно. Поэтому я не могу добавить функцию и закрыть ее вручную в функции. Любой шаблон, который может сделать это для меня?
Ответы
Ответ 1
Вот функция, которую я использовал раньше, которая работает очень хорошо:
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;
}
Лично я бы этого не сделал, используя regexp, но такую библиотеку, как 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;
Ответ 2
Небольшая модификация исходного ответа... в то время как исходный ответ правильно разделил теги. Я обнаружил, что во время моего усечения я мог бы в итоге нарезать теги. Например:
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
Существует множество других переменных, которые необходимо адресовать, чтобы дать полное решение, но не охвачены вашим вопросом.
Однако я бы предложил использовать что-то вроде HTML Tidy и, в частности, методы repairFile
или repaireString
.
Ответ 4
Этот метод 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;
}
Ответ 5
А как насчет использования 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 выдает предупреждение "недопустимый тег в Entity", если используются HTML5 или пользовательские теги (тем не менее, он все равно будет работать правильно).
Ответ 6
Использование регулярного выражения для этого не является идеальным подходом. Вместо этого вы должны использовать html-парсер для создания допустимой модели объекта документа.
В качестве второго варианта, в зависимости от того, что вы хотите, вы можете использовать регулярное выражение для удаления любых тэгов html из вашей строки, прежде чем поместить их в тег <p>
.
Ответ 7
Я сделал этот код, чтобы сделать работу совершенно правильно...
Это старая школа, но эффективная, и я добавил флаг для удаления незавершенных тегов, таких как "blah blah 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 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>";
}
Ответ 8
если установлен модуль tidy, используйте расширение php tidy:
tidy_repair_string($html)
ссылка
Ответ 9
Это работает для меня, чтобы закрыть все открытые теги 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;
}