it-swarm-ru.tech

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

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

170
Brian

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

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

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

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

212
Grey Panther

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

preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, 201));
125
mattmac
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

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

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

42
Dave

Следующее решение появилось, когда я заметил параметр $ 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.
33
Sergiy Sokolenko

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

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

9
Garrett Albright

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

8
Lucas Oman

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

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

  return $string;
}
5
Camsoft

Ну вот:

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

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

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

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

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

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

Последний вариант, если вы хотите добавить Ellipsis, если он обрезает строку ... 

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
orrd
$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.comorrrr ровно 200 символов.
  3. regex101.com после пятой rorrrrr исключается.

Наслаждаться.

3
hlcs

Итак, я получил другую версию этого, основанную на ответах выше, но принимая во внимание больше вещей (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;
  }
2
Yo-L
/*
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 относятся к нам прямо сейчас.

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

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

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

2
bodi0

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

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

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

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

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

$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")));
1
Shashank Saxena

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

<?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;
}
?>
1
Rikudou_Sennin

Основано на регулярном выражении @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]. '...';
}
1
amateur barista

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

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

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

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

1
tanc

Добавлены операторы 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);
}
0
jdorenbush

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

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

Я создаю функцию, более похожую на 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 .: Полная длина реза может быть меньше, чем длина.

0
evandro777

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

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

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

0
Namida

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

<?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;
?>
0
Yousef Altaf

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

функция abbreviate_string_to_whole_Word ($ string, $ max_length, $ buffer) {

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;

}

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

0
Mat Barnett

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

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

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

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

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