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

У меня есть различные строки 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
поделиться

6 ответов

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

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);
38
ответ дан 27 November 2019 в 00:40
поделиться

Я не утверждаю, что изобрел это, но в 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;
}
16
ответ дан 27 November 2019 в 00:40
поделиться

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

3
ответ дан 27 November 2019 в 00:40
поделиться

Независимо от того, что вы указали 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;
    }  
}

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

1
ответ дан 27 November 2019 в 00:40
поделиться

Используйте класс 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, были разработаны для выявления и решения этих проблем и являются гораздо более полными, чем любой код, который я мог бы опубликовать.

7
ответ дан 27 November 2019 в 00:40
поделиться

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

( Кредиты, причитающиеся )

2
ответ дан 27 November 2019 в 00:40
поделиться
Другие вопросы по тегам:

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