Разбить строку HTML на две строки, не разбивая слово и не сохраняя HTML в PHP - PullRequest
3 голосов
/ 01 апреля 2010

Я ищу способ разбить строку, содержащую HTML, на две половины. Требования:

  • Разделить строку по числу символов
  • Не должен разбиваться на середину слова
  • Не должен включать символы HTML при расчете, где разбивать строку

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

<p>This is a test string that contains <strong>HTML</strong> tags and text content. This string needs to be split without slicing through the <em>middle</em> of a word and must preserve the validity of the HTML, i.e. not split in the middle of a tag, and make sure closing tags are respected correctly.</p>

Скажем, я хочу разделить в позиции символа 39, середине слова HTML (не считая html), я хотел бы, чтобы функция разбивала строку на следующие две части:

<p>This is a test string that contains <strong>HTML</strong></p>

и

<p>tags and text content. This string needs to be split without slicing through the <em>middle</em> of a word and must preserve the validity of the HTML, i.e. not split in the middle of a tag, and make sure closing tags are respected correctly.</p>

Обратите внимание, что в приведенных выше двух примерах результаты требуют соблюдения допустимости HTML, поэтому были добавлены закрывающие теги </strong> и </p>. Также во второй половине был добавлен начальный тег <p>, закрываемый в конце строки.

Я обнаружил, что эта функция в StackOverflow усекает строку по количеству текстовых символов и сохраняет HTML, но она идет только наполовину, когда мне нужно, поскольку мне нужно разделить на две половины.

function printTruncated($maxLength, $html)
{
    $printedLength = 0;
    $position = 0;
    $tags = array();

    while ($printedLength < $maxLength && preg_match('{</?([a-z]+)[^>]*>|&#?[a-zA-Z0-9]+;}', $html, $match, PREG_OFFSET_CAPTURE, $position))
    {
        list($tag, $tagPosition) = $match[0];

        // Print text leading up to the tag.
        $str = substr($html, $position, $tagPosition - $position);
        if ($printedLength + strlen($str) > $maxLength)
        {
            print(substr($str, 0, $maxLength - $printedLength));
            $printedLength = $maxLength;
            break;
        }

        print($str);
        $printedLength += strlen($str);

        if ($tag[0] == '&')
        {
            // Handle the entity.
            print($tag);
            $printedLength++;
        }
        else
        {
            // Handle the tag.
            $tagName = $match[1][0];
            if ($tag[1] == '/')
            {
                // This is a closing tag.

                $openingTag = array_pop($tags);
                assert($openingTag == $tagName); // check that tags are properly nested.

                print($tag);
            }
            else if ($tag[strlen($tag) - 2] == '/')
            {
                // Self-closing tag.
                print($tag);
            }
            else
            {
                // Opening tag.
                print($tag);
                $tags[] = $tagName;
            }
        }

        // Continue after the tag.
        $position = $tagPosition + strlen($tag);
    }

    // Print any remaining text.
    if ($printedLength < $maxLength && $position < strlen($html))
        print(substr($html, $position, $maxLength - $printedLength));

    // Close any open tags.
    while (!empty($tags))
        printf('</%s>', array_pop($tags));
}

1 Ответ

4 голосов
/ 28 ноября 2010

Общее правило, на которое вы будете цитировать почти все остальные ответы: «Не обрабатывайте HTML с помощью регулярных выражений - вы не можете охватить все крайние случаи»

Я верю, что это правда

Что-нибудь, даже немного искаженное в вашей строке, и даже самое лучшее регулярное выражение все равно испортит его

Не обращая внимания на то, что вы хотите разделить некоторые теги, а не другие (в конце концов, p-теги - это теги, и вы хотите разделить один тег на два), вам, возможно, потребуется переосмыслить процесс и получить очень конкретную информацию о том, что вы хотите достичь, например, расщепление в середине тега абзаца хорошо? Что насчет дивов? Если средняя точка находится внутри тега, хотите ли вы, чтобы первая строка была длиннее или вторая?

Предполагая, что разделение тегов абзаца в порядке, а другие нет, попробуйте следующий подход: (здесь нет кода для копирования-вставки, извините) * Обрезать целевую строку дважды - один раз из всех тегов и один раз только из тегов абзаца * Найти среднюю точку в строке no-tags-at-all * Разделить строку no-tags-at-all на первый пробел после средней точки * Найдите место в строке just-p-tags-stripped, которое соответствует слову / словам сразу после средней точки на предыдущем шаге - это должно сказать вам, где в строке just-p-tags-stripped находится «середина», когда теги игнорируются * Проверьте, если вы внутри тега.

.. на самом деле, как только я дошел до этого момента, я понял, что 90% того, что я написал, чертовски очевидно, и что последняя точка находится именно там, где проблема

Я собираюсь оставить здесь свою наполовину законченную напыщенную речь, предупреждая других и себя ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...