У меня есть различные строки HTML для вырезания к 100 символам (разделенного содержания, не оригинала), не разделяя теги и не повреждая HTML.
Исходная строка HTML (288 символов):
$content = "With a span over here and a
nested div over there
and a lot of other nested texts and tags in the air
everywhere, it's a HTML taggy kind of day.";
Стандартная обрезка: Обрезка к 100 символам и повреждениям HTML, разделенное содержание доходит до ~40 символов:
$content = substr($content, 0, 100)."..."; /* output:
With a span over here and a
nested div ove... */
Разделенный 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 или очиститель для закрытия выводов тегов чистит HTML, но 100 символов HTML не отображенное содержание.
$content = substr($content, 0, 100)."...";
$tidy = new tidy; $tidy->parseString($content); $tidy->cleanRepair(); /* output:
With a span over here and a
nested div ove... */
Проблема: производить чистый HTML и n символы (исключая счетчик символов элементов HTML):
$content = cutHTML($content, 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...";
Подобные вопросы
41
задан Community 23 May 2017 в 12:34
поделиться
Не удивительно, но работает.
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('/<\/([a-z]+)>/', $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;
}
Используйте HTML-парсер и останавливайтесь после 100 символов текста.
Независимо от того, что вы указали 100 проблем с подсчетом в начале вы указываете в задаче следующее:
Вот мое предложение: В основном, я анализирую каждый символ, считая по ходу. Я НЕ считаю какие-либо символы в любом теге 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;
}
}
Также будут подсчитаны коды возврата и т. Д. Поскольку они занимают место, я их не удалял.
Используйте класс PHP DOMDocument для нормализации 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()"
или ...
. Такие инструменты, как HTML Purifier, были разработаны для выявления и решения этих проблем и являются гораздо более полными, чем любой код, который я мог бы опубликовать.
Вам следует использовать Tidy HTML . Вы разрезаете строку, а затем запускаете Tidy, чтобы закрыть теги.