Как усечь строку в PHP до слова, ближайшего к определенному числу символов?

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

Ответ 1

Используя функцию 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);

Ответ 2

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

preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, 201));

Ответ 3

$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

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

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

Ответ 4

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

string wordwrap (string $str [, int $width = 75 [, string $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.

Ответ 5

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

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

Ответ 6

Используйте 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 символов.

Ответ 7

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

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

  return $string;
}

Ответ 8

Здесь вы идете:

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;
   }
}

Ответ 9

$shorttext = preg_replace('/^([\s\S]{1,200})[\s]+?[\s\S]+/', '$1', $fulltext);

Описание:

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

Тесты:

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

Enjoy.

Ответ 10

Удивительно, как сложно найти идеальное решение этой проблемы. Я еще не нашел ответа на этой странице, который не сработает, по крайней мере, в некоторых ситуациях (особенно если строка содержит символы новой строки или вкладки, или если слово break является чем-то другим, кроме пробела, или если строка имеет 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;
}

Ответ 11

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

$matches = array();
$result = preg_match("/^(.{1,199})[\s]/i", $text, $matches);

Выражение означает "соответствовать любой подстроке, начиная с начала длины 1-200, которая заканчивается пробелом". Результат получается в $result, а совпадение - в $match. Это позаботится о вашем первоначальном вопросе, который определенно заканчивается на любом пространстве. Если вы хотите, чтобы он заканчивался на символах новой строки, измените регулярное выражение на:

$result = preg_match("/^(.{1,199})[\n]/i", $text, $matches);

Ответ 12

Хорошо, поэтому я получил еще одну версию этого, основываясь на приведенных выше ответах, но занимая больше вещей в учетной записи (utf-8,\n и & nbsp;), а также строку, разделяющую короткие коды wordpress, прокомментированные, если они используются с wp.

function neatest_trim($content, $chars) 
  if (strlen($content) > $chars) 
  {
    $content = str_replace('&nbsp;', ' ', $content);
    $content = str_replace("\n", '', $content);
    // use with wordpress    
    //$content = strip_tags(strip_shortcodes(trim($content)));
    $content = strip_tags(trim($content));
    $content = preg_replace('/\s+?(\S+)?$/', '', mb_substr($content, 0, $chars));

    $content = trim($content) . '...';
    return $content;
  }

Ответ 13

/*
Cut the string without breaking any words, UTF-8 aware 
* param string $str The text string to split
* param integer $start The start position, defaults to 0
* param integer $words The number of words to extract, defaults to 15
*/
function wordCutString($str, $start = 0, $words = 15 ) {
    $arr = preg_split("/[\s]+/",  $str, $words+1);
    $arr = array_slice($arr, $start, $words);
    return join(' ', $arr);
}

Использование:

$input = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna liqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.';
echo wordCutString($input, 0, 10); 

Это будет выводить первые 10 слов.

Функция preg_split используется для разбиения строки на подстроки. Границы, вдоль которых должна быть разбита строка, задаются с использованием шаблона регулярных выражений.

preg_split функция принимает 4 параметра, но только первые 3 относятся к нам прямо сейчас.

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

Второй параметр - строка ввода Второй параметр - длинная текстовая строка, которую мы хотим разделить.

Третий параметр - лимит Третий параметр указывает количество подстрок, которые должны быть возвращены. Если вы установите предел на n, preg_split вернет массив из n элементов. Первые элементы n-1 будут содержать подстроки. Последний элемент (n th) будет содержать остальную часть строки.

Ответ 14

На основе регулярного выражения @Justin Poliey:

// Trim very long text to 120 characters. Add an ellipsis if the text is trimmed.
if(strlen($very_long_text) > 120) {
  $matches = array();
  preg_match("/^(.{1,120})[\s]/i", $very_long_text, $matches);
  $trimmed_text = $matches[0]. '...';
}

Ответ 15

Это небольшое исправление для ответа mattmac:

preg_replace('/\s+?(\S+)?$/', '', substr($string . ' ', 0, 201));

Единственное отличие состоит в том, чтобы добавить пробел в конец строки $. Это гарантирует, что последнее слово не будет отключено в соответствии с комментарием ReX357.

У меня недостаточно точек rep, чтобы добавить это как комментарий.

Ответ 16

У меня есть функция, которая делает почти то, что вы хотите, если вы сделаете несколько изменений, она будет точно соответствовать:

<?php
function stripByWords($string,$length,$delimiter = '<br>') {
    $words_array = explode(" ",$string);
    $strlen = 0;
    $return = '';
    foreach($words_array as $word) {
        $strlen += mb_strlen($word,'utf8');
        $return .= $word." ";
        if($strlen >= $length) {
            $strlen = 0;
            $return .= $delimiter;
        }
    }
    return $return;
}
?>

Ответ 17

Вот как я это сделал:

$string = "I appreciate your service & idea to provide the branded toys at a fair rent price. This is really a wonderful to watch the kid not just playing with variety of toys but learning faster compare to the other kids who are not using the BooksandBeyond service. We wish you all the best";

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

Ответ 18

Я знаю, что это старый, но...

function _truncate($str, $limit) {
    if(strlen($str) < $limit)
        return $str;
    $uid = uniqid();
    return array_shift(explode($uid, wordwrap($str, $limit, $uid)));
}

Ответ 19

Я использовал это раньше

<?php
    $your_desired_width = 200;
    $string = $var->content;
    if (strlen($string) > $your_desired_width) {
        $string = wordwrap($string, $your_desired_width);
        $string = substr($string, 0, strpos($string, "\n")) . " More...";
    }
    echo $string;
?>

Ответ 20

Я создаю функцию, более похожую на substr, и используя идею @Dave.

function substr_full_word($str, $start, $end){
    $pos_ini = ($start == 0) ? $start : stripos(substr($str, $start, $end), ' ') + $start;
    if(strlen($str) > $end){ $pos_end = strrpos(substr($str, 0, ($end + 1)), ' '); } // IF STRING SIZE IS LESSER THAN END
    if(empty($pos_end)){ $pos_end = $end; } // FALLBACK
    return substr($str, $pos_ini, $pos_end);
}

Ps: Полное сокращение длины может быть меньше, чем substr.

Ответ 21

Добавлены инструкции IF/ELSEIF в код из Dave и AmalMurali для обработки строк без пробелов

if ((strpos($string, ' ') !== false) && (strlen($string) > 200)) { 
    $WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' ')); 
} 
elseif (strlen($string) > 200) {
    $WidgetText = substr($string, 0, 200);
}

Ответ 22

Я считаю, что это самый простой способ сделать это:

$lines = explode('♦♣♠',wordwrap($string, $length, '♦♣♠'));
$newstring = $lines[0] . ' &bull; &bull; &bull;';

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

Ответ 23

Я считаю это работает:

функция abbreviate_string_to_whole_word ($ строка, $ max_length, $ буфер) {

if (strlen($string)>$max_length) {
    $string_cropped=substr($string,0,$max_length-$buffer);
    $last_space=strrpos($string_cropped, " ");
    if ($last_space>0) {
        $string_cropped=substr($string_cropped,0,$last_space);
    }
    $abbreviated_string=$string_cropped."&nbsp;...";
}
else {
    $abbreviated_string=$string;
}

return $abbreviated_string;

}

Буфер позволяет вам настроить длину возвращаемой строки.

Ответ 24

Использовать этот:

следующий код удалит ','. Если у вас есть какой-либо другой символ или подстрока, вы можете использовать это вместо ','

substr($string, 0, strrpos(substr($string, 0, $comparingLength), ','))

//если у вас есть другая строковая учетная запись для

substr($string, 0, strrpos(substr($string, 0, $comparingLength-strlen($currentString)), ','))

Ответ 25

Здесь вы можете попробовать это

substr( $str, 0, strpos($str, ' ', 200) ); 

Ответ 26

Возможно, это поможет кому-то:

<?php

    $string = "Your line of text";
    $spl = preg_match("/([, \.\d\-''\"\"_()]*\w+[, \.\d\-''\"\"_()]*){50}/", $string, $matches);
    if (isset($matches[0])) {
        $matches[0] .= "...";
        echo "<br />" . $matches[0];
    } else {
        echo "<br />" . $string;
    }

?>