Закрыть открытые 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> 

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

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

 function closetags($html) { preg_match_all('#<(?!meta|img|br|hr|input\b)\b([az]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result); $openedtags = $result[1]; preg_match_all('#</([az]+)>#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; 

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

 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([az]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result); $openedtags = $result[1]; preg_match_all('#</([az]+)>#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"; 

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

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

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

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

 function closetags($html) { preg_match_all('#<([az]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result); $openedtags = $result[1]; preg_match_all('#</([az]+)>#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; } 

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

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

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

Это старая школа, но эффективная, и я добавил флаг для удаления незавершенных тегов, таких как «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'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>"; } 

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

 tidy_repair_string($html) 

Справка