Как обрезать строку в PHP до слова, ближайшего к определенному количеству символов? - PullRequest
171 голосов
/ 17 сентября 2008

У меня есть фрагмент кода, написанный на PHP, который извлекает блок текста из базы данных и отправляет его виджету на веб-странице. Оригинальный блок текста может быть длинной статьей или коротким предложением или двумя; но для этого виджета я не могу отобразить больше, скажем, 200 символов. Я мог бы использовать substr (), чтобы обрезать текст на 200 символов, но результат был бы обрезан в середине слова - что я действительно хочу - обрезать текст в конце последнего слова до 200 символов.

Ответы [ 26 ]

212 голосов
/ 17 сентября 2008

С помощью функции wordwrap . Он разбивает текст на несколько строк так, чтобы максимальная ширина была той, которую вы указали, ломаясь за границы слов. После разделения вы просто берете первую строку:

substr($string, 0, strpos(wordwrap($string, $your_desired_width), "\n"));

Одна вещь, которую этот oneliner не обрабатывает, - это случай, когда сам текст короче желаемой ширины. Чтобы обработать этот крайний случай, нужно сделать что-то вроде:

if (strlen($string) > $your_desired_width) 
{
    $string = wordwrap($string, $your_desired_width);
    $string = substr($string, 0, strpos($string, "\n"));
}

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

function tokenTruncate($string, $your_desired_width) {
  $parts = preg_split('/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE);
  $parts_count = count($parts);

  $length = 0;
  $last_part = 0;
  for (; $last_part < $parts_count; ++$last_part) {
    $length += strlen($parts[$last_part]);
    if ($length > $your_desired_width) { break; }
  }

  return implode(array_slice($parts, 0, $last_part));
}

Также вот тестовый класс PHPUnit, используемый для тестирования реализации:

class TokenTruncateTest extends PHPUnit_Framework_TestCase {
  public function testBasic() {
    $this->assertEquals("1 3 5 7 9 ",
      tokenTruncate("1 3 5 7 9 11 14", 10));
  }

  public function testEmptyString() {
    $this->assertEquals("",
      tokenTruncate("", 10));
  }

  public function testShortString() {
    $this->assertEquals("1 3",
      tokenTruncate("1 3", 10));
  }

  public function testStringTooLong() {
    $this->assertEquals("",
      tokenTruncate("toooooooooooolooooong", 10));
  }

  public function testContainingNewline() {
    $this->assertEquals("1 3\n5 7 9 ",
      tokenTruncate("1 3\n5 7 9 11 14", 10));
  }
}

РЕДАКТИРОВАТЬ:

Специальные символы UTF8, такие как «а», не обрабатываются. Добавьте 'u' в конце REGEX для обработки:

$parts = preg_split('/([\s\n\r]+)/u', $string, null, PREG_SPLIT_DELIM_CAPTURE);

131 голосов
/ 17 сентября 2008

Это вернет первые 200 символов слов:

preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, 201));
42 голосов
/ 12 января 2011
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

И вот он - надежный метод обрезания любой строки до ближайшего целого слова, оставаясь при максимальной длине строки.

Я пробовал другие примеры выше, и они не дали желаемых результатов.

34 голосов
/ 25 июля 2013

Следующее решение появилось, когда я заметил параметр $ break в wordwrap function:

строка wordwrap (строка $ str [, int $ width = 75 [, строка $ break = "\ n" [, bool $ cut = false]]])

Вот решение :

/**
 * Truncates the given string at the specified length.
 *
 * @param string $str The input string.
 * @param int $width The number of chars at which the string will be truncated.
 * @return string
 */
function truncate($str, $width) {
    return strtok(wordwrap($str, $width, "...\n"), "\n");
}

Пример №1.

print truncate("This is very long string with many chars.", 25);

Приведенный выше пример выдаст:

This is very long string...

Пример № 2.

print truncate("This is short string.", 25);

Приведенный выше пример выведет:

This is short string.
9 голосов
/ 17 сентября 2008

Имейте в виду, что когда вы разбиваете слово "везде", некоторые языки, такие как китайский и японский, не используют пробел для разделения слов. Кроме того, злонамеренный пользователь может просто ввести текст без пробелов или использовать некоторый аналог Unicode со стандартным пробелом, и в этом случае любое используемое вами решение может в конечном итоге отобразить весь текст в любом случае. Обходным путем может быть проверка длины строки после разбиения ее на обычные пробелы, затем, если строка все еще превышает ненормальный предел - в данном случае, возможно, 225 символов, - продолжая и тупо разделяя ее на этом пределе.

Еще одна оговорка с такими вещами, когда речь идет о не-ASCII символах; Строки, содержащие их, могут быть интерпретированы стандартным PHP strlen () как более длинные, чем они есть на самом деле, потому что один символ может занимать два или более байтов вместо одного. Если вы просто используете функции strlen () / substr () для разделения строк, вы можете разделить строку в середине символа! В случае сомнений mb_strlen () / mb_substr () немного более надежно.

8 голосов
/ 17 сентября 2008

Используйте strpos и substr:

<?php

$longString = "I have a code snippet written in PHP that pulls a block of text.";
$truncated = substr($longString,0,strpos($longString,' ',30));

echo $truncated;

Это даст вам обрезанную строку в первом пробеле после 30 символов.

5 голосов
/ 26 марта 2010

Вот моя функция, основанная на подходе @ Cd-MaN.

function shorten($string, $width) {
  if(strlen($string) > $width) {
    $string = wordwrap($string, $width);
    $string = substr($string, 0, strpos($string, "\n"));
  }

  return $string;
}
4 голосов
/ 17 сентября 2008

Вот, пожалуйста:

function neat_trim($str, $n, $delim='…') {
   $len = strlen($str);
   if ($len > $n) {
       preg_match('/(.{' . $n . '}.*?)\b/', $str, $matches);
       return rtrim($matches[1]) . $delim;
   }
   else {
       return $str;
   }
}
3 голосов
/ 01 сентября 2015

Удивительно, насколько сложно найти идеальное решение этой проблемы. Я еще не нашел ответ на этой странице, который не дает сбой, по крайней мере, в некоторых ситуациях (особенно, если строка содержит символы новой строки или табуляции, или если разрыв слова - это что-то отличное от пробела, или если строка имеет UTF- 8 многобайтовых символов).

Вот простое решение, которое работает во всех случаях. Здесь были похожие ответы, но модификатор "s" важен, если вы хотите, чтобы он работал с многострочным вводом, а модификатор "u" позволяет правильно оценивать многобайтовые символы UTF-8.

function wholeWordTruncate($s, $characterCount) 
{
    if (preg_match("/^.{1,$characterCount}\b/su", $s, $match)) return $match[0];
    return $s;
}

Один возможный крайний случай с этим ... если в строке вообще нет пробелов в первых символах $ characterCount, она вернет всю строку. Если вы предпочитаете, чтобы это вызывало разрыв в $ characterCount, даже если это не граница слова, вы можете использовать это:

function wholeWordTruncate($s, $characterCount) 
{
    if (preg_match("/^.{1,$characterCount}\b/su", $s, $match)) return $match[0];
    return mb_substr($return, 0, $characterCount);
}

Последний вариант, если вы хотите, чтобы он добавлял многоточие, если он обрезает строку ...

function wholeWordTruncate($s, $characterCount, $addEllipsis = ' …') 
{
    $return = $s;
    if (preg_match("/^.{1,$characterCount}\b/su", $s, $match)) 
        $return = $match[0];
    else
        $return = mb_substr($return, 0, $characterCount);
    if (strlen($s) > strlen($return)) $return .= $addEllipsis;
    return $return;
}
3 голосов
/ 03 июля 2014
$shorttext = preg_replace('/^([\s\S]{1,200})[\s]+?[\s\S]+/', '$1', $fulltext);

Описание:

  • ^ - начало с начала строки
  • ([\s\S]{1,200}) - получить от 1 до 200 любого персонажа
  • [\s]+? - не включать пробелы в конце короткого текста, поэтому мы можем избежать word ... вместо word...
  • [\s\S]+ - сопоставить все остальное содержимое

Тесты:

  1. regex101.com давайте добавим к or несколько других r
  2. regex101.com orrrr ровно 200 символов.
  3. regex101.com после пятого r orrrrr исключено.

Наслаждайтесь.

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