Использование PHP substr () и strip_tags () при сохранении форматирования и без нарушения HTML

У меня есть различные строки HTML, чтобы вырезать до 100 символов (отделяемого контента, а не оригинала) без снятия тегов и без нарушения HTML.

Исходная строка HTML (288 символов):

$content = "<div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div over <div class='nestedDivClass'>there</div> </div> and a lot of other nested <strong><em>texts</em> and tags in the air <span>everywhere</span>, it's a HTML taggy kind of day.</strong></div>"; 

Стандартная обрезка: обрезать до 100 символов и разрывов HTML, лишенный контент доходит до ~ 40 символов:

 $content = substr($content, 0, 100)."..."; /* output: <div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div ove... */ 

Stripped HTML: выводит правильный счетчик символов, но явно теряет форматирование:

 $content = substr(strip_tags($content)), 0, 100)."..."; /* output: With a span over here and a nested div over there and a lot of other nested texts and tags in the ai... */ 

Частичное решение: использование HTML Tidy или очистителя для закрытия тегов выводит чистый HTML, но 100 символов HTML не отображается.

 $content = substr($content, 0, 100)."..."; $tidy = new tidy; $tidy->parseString($content); $tidy->cleanRepair(); /* output: <div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div ove</div></div>... */ 

Задача: выводить чистые символы HTML и n (исключая количество символов HTML-элементов):

 $content = cutHTML($content, 100); /* output: <div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div over <div class='nestedDivClass'>there</div> </div> and a lot of other nested <strong><em>texts</em> and tags in the ai</strong></div>..."; 

Похожие вопросы

  • Как обрезать фрагменты HTML без разбивки тегов
  • Вырезание строк HTML без нарушения HTML-тегов

Related of "Использование PHP substr () и strip_tags () при сохранении форматирования и без нарушения HTML"

Не удивительно, но работает.

 function html_cut($text, $max_length) { $tags = array(); $result = ""; $is_open = false; $grab_open = false; $is_close = false; $in_double_quotes = false; $in_single_quotes = false; $tag = ""; $i = 0; $stripped = 0; $stripped_text = strip_tags($text); while ($i < strlen($text) && $stripped < strlen($stripped_text) && $stripped < $max_length) { $symbol = $text{$i}; $result .= $symbol; switch ($symbol) { case '<': $is_open = true; $grab_open = true; break; case '"': if ($in_double_quotes) $in_double_quotes = false; else $in_double_quotes = true; break; case "'": if ($in_single_quotes) $in_single_quotes = false; else $in_single_quotes = true; break; case '/': if ($is_open && !$in_double_quotes && !$in_single_quotes) { $is_close = true; $is_open = false; $grab_open = false; } break; case ' ': if ($is_open) $grab_open = false; else $stripped++; break; case '>': if ($is_open) { $is_open = false; $grab_open = false; array_push($tags, $tag); $tag = ""; } else if ($is_close) { $is_close = false; array_pop($tags); $tag = ""; } break; default: if ($grab_open || $is_close) $tag .= $symbol; if (!$is_open && !$is_close) $stripped++; } $i++; } while ($tags) $result .= "</".array_pop($tags).">"; return $result; } 

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

 $content = html_cut($content, 100); 

Я не утверждаю, что изобрел это, но в CakePHP существует очень полный метод Text::truncate() который делает то, что вы хотите:

 function truncate($text, $length = 100, $ending = '...', $exact = true, $considerHtml = false) { if (is_array($ending)) { extract($ending); } if ($considerHtml) { if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) { return $text; } $totalLength = mb_strlen($ending); $openTags = array(); $truncate = ''; preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER); foreach ($tags as $tag) { if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) { if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) { array_unshift($openTags, $tag[2]); } else if (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) { $pos = array_search($closeTag[1], $openTags); if ($pos !== false) { array_splice($openTags, $pos, 1); } } } $truncate .= $tag[1]; $contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3])); if ($contentLength + $totalLength > $length) { $left = $length - $totalLength; $entitiesLength = 0; if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) { foreach ($entities[0] as $entity) { if ($entity[1] + 1 - $entitiesLength <= $left) { $left--; $entitiesLength += mb_strlen($entity[0]); } else { break; } } } $truncate .= mb_substr($tag[3], 0 , $left + $entitiesLength); break; } else { $truncate .= $tag[3]; $totalLength += $contentLength; } if ($totalLength >= $length) { break; } } } else { if (mb_strlen($text) <= $length) { return $text; } else { $truncate = mb_substr($text, 0, $length - strlen($ending)); } } if (!$exact) { $spacepos = mb_strrpos($truncate, ' '); if (isset($spacepos)) { if ($considerHtml) { $bits = mb_substr($truncate, $spacepos); preg_match_all('/<\/([az]+)>/', $bits, $droppedTags, PREG_SET_ORDER); if (!empty($droppedTags)) { foreach ($droppedTags as $closingTag) { if (!in_array($closingTag[1], $openTags)) { array_unshift($openTags, $closingTag[1]); } } } } $truncate = mb_substr($truncate, 0, $spacepos); } } $truncate .= $ending; if ($considerHtml) { foreach ($openTags as $tag) { $truncate .= '</'.$tag.'>'; } } return $truncate; } 

Используйте класс DOMDocument PHP для нормализации фрагмента HTML:

 $dom= new DOMDocument(); $dom->loadHTML('<div><p>Hello World'); $xpath = new DOMXPath($dom); $body = $xpath->query('/html/body'); echo($dom->saveXml($body->item(0))); 

Этот вопрос похож на предыдущий вопрос, и я скопировал и вставил одно решение здесь. Если HTML отправлен пользователями, вам также потребуется отфильтровать потенциальные векторы атаки Javascript, такие как onmouseover="do_something_evil()" или <a href="javascript:more_evil();">...</a> . Такие инструменты, как HTML Purifier, были разработаны, чтобы поймать и решить эти проблемы и гораздо более всеобъемлющи, чем любой код, который я мог бы опубликовать.

Используйте парсер HTML и остановите после 100 символов текста.

Вы должны использовать Tidy HTML . Вы сокращаете строку, а затем запускаете Tidy, чтобы закрыть теги.

( Кредиты, в которых причитаются кредиты )

Я сделал еще одну функцию для этого, поддерживает UTF-8:

 /** * Limit string without break html tags. * Supports UTF8 * * @param string $value * @param int $limit Default 100 */ function str_limit_html($value, $limit = 100) { if (mb_strwidth($value, 'UTF-8') <= $limit) { return $value; } // Strip text with HTML tags, sum html len tags too. // Is there another way to do it? do { $len = mb_strwidth($value, 'UTF-8'); $len_stripped = mb_strwidth(strip_tags($value), 'UTF-8'); $len_tags = $len - $len_stripped; $value = mb_strimwidth($value, 0, $limit + $len_tags, '', 'UTF-8'); } while ($len_stripped > $limit); // Load as HTML ignoring errors $dom = new DOMDocument(); @$dom->loadHTML('<?xml encoding="utf-8" ?>'.$value, LIBXML_HTML_NODEFDTD); // Fix the html errors $value = $dom->saveHtml($dom->getElementsByTagName('body')->item(0)); // Remove body tag $value = mb_strimwidth($value, 6, mb_strwidth($value, 'UTF-8') - 13, '', 'UTF-8'); // <body> and </body> // Remove empty tags return preg_replace('/<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|"[^"]*"|[\w\-.:]+))?)*\s*\/?>\s*<\/\1\s*>/', '', $value); } 

СМОТРИТЕ ДЕМО .

Я рекомендую использовать html_entity_decode в начале функции, поэтому сохраните символы UTF-8:

  $value = html_entity_decode($value); 

Независимо от 100 вопросов подсчета, которые вы указываете в начале, вы указываете в вызове следующее:

  • выводит количество символов strip_tags (количество символов в фактическом отображаемом тексте HTML)
  • сохранить форматирование HTML закрыть
  • любой незавершенный тег HTML

Вот мое предложение: Баснически, я анализирую каждый персонаж, подсчитывающий, когда я иду. Я удостоверяюсь, что НЕ подсчитывать любые символы в любом теге HTML. Я также проверяю в конце, чтобы убедиться, что я не посередине, когда останавливаюсь. Как только я остановлюсь, я вернусь к первому доступному пространству или> как к точке остановки.

 $position = 0; $length = strlen($content)-1; // process the content putting each 100 character section into an array while($position < $length) { $next_position = get_position($content, $position, 100); $data[] = substr($content, $position, $next_position); $position = $next_position; } // show the array print_r($data); function get_position($content, $position, $chars = 100) { $count = 0; // count to 100 characters skipping over all of the HTML while($count <> $chars){ $char = substr($content, $position, 1); if($char == '<'){ do{ $position++; $char = substr($content, $position, 1); } while($char !== '>'); $position++; $char = substr($content, $position, 1); } $count++; $position++; } echo $count."\n"; // find out where there is a logical break before 100 characters $data = substr($content, 0, $position); $space = strrpos($data, " "); $tag = strrpos($data, ">"); // return the position of the logical break if($space > $tag) { return $space; } else { return $tag; } } 

Это также будет учитывать коды возврата и т. Д. Учитывая, что они занимают место, я их не удалял.

Вот функция, которую я использую в одном из моих проектов. Он основан на DOMDocument, работает с HTML5 и примерно в 2 раза быстрее, чем другие решения, которые я пробовал (по крайней мере на моей машине, 0,22 мс против 0,43 мс, используя html_cut($text, $max_length) из верхнего ответа на 500 текстовых сообщений, строка узловых символов с лимитом 400).

 function cut_html ($html, $limit) { $dom = new DOMDocument(); $dom->loadHTML(mb_convert_encoding("<div>{$html}</div>", "HTML-ENTITIES", "UTF-8"), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); cut_html_recursive($dom->documentElement, $limit); return substr($dom->saveHTML($dom->documentElement), 5, -6); } function cut_html_recursive ($element, $limit) { if($limit > 0) { if($element->nodeType == 3) { $limit -= strlen($element->nodeValue); if($limit < 0) { $element->nodeValue = substr($element->nodeValue, 0, strlen($element->nodeValue) + $limit); } } else { for($i = 0; $i < $element->childNodes->length; $i++) { if($limit > 0) { $limit = cut_html_recursive($element->childNodes->item($i), $limit); } else { $element->removeChild($element->childNodes->item($i)); $i--; } } } } return $limit; } 

Вот моя попытка на резаке. Возможно, вы, ребята, можете поймать некоторые ошибки. Проблема, которую я нашел с другими синтаксическими анализаторами, заключается в том, что они не закрывают теги должным образом, и они разрезают посреди слова (бла)

 function cutHTML($string, $length, $patternsReplace = false) { $i = 0; $count = 0; $isParagraphCut = false; $htmlOpen = false; $openTag = false; $tagsStack = array(); while ($i < strlen($string)) { $char = substr($string, $i, 1); if ($count >= $length) { $isParagraphCut = true; break; } if ($htmlOpen) { if ($char === ">") { $htmlOpen = false; } } else { if ($char === "<") { $j = $i; $char = substr($string, $j, 1); while ($j < strlen($string)) { if($char === '/'){ $i++; break; } elseif ($char === ' ') { $tagsStack[] = substr($string, $i, $j); } $j++; } $htmlOpen = true; } } if (!$htmlOpen && $char != ">") { $count++; } $i++; } if ($isParagraphCut) { $j = $i; while ($j > 0) { $char = substr($string, $j, 1); if ($char === " " || $char === ";" || $char === "." || $char === "," || $char === "<" || $char === "(" || $char === "[") { break; } else if ($char === ">") { $j++; break; } $j--; } $string = substr($string, 0, $j); foreach($tagsStack as $tag){ $tag = strtolower($tag); if($tag !== "img" && $tag !== "br"){ $string .= "</$tag>"; } } $string .= "..."; } if ($patternsReplace) { foreach ($patternsReplace as $value) { if (isset($value['pattern']) && isset($value["replace"])) { $string = preg_replace($value["pattern"], $value["replace"], $string); } } } return $string; } 

попробуйте эту функцию

 // trim the string function function trim_word($text, $length, $startPoint=0, $allowedTags=""){ $text = html_entity_decode(htmlspecialchars_decode($text)); $text = strip_tags($text, $allowedTags); return $text = substr($text, $startPoint, $length); } 

а также

 echo trim_word("<h2 class='zzzz'>abcasdsdasasdas</h2>","6");