Я пытаюсь обрезать длинную строку до определенного количества символов и интерполировать другую пользовательскую строку в середине (более или менее), чтобы показать, что строка была обрезана. И в то же время я пытаюсь сделать так, чтобы слова не разбивались пополам. Например:
Быстрая, коричневая лиса, перепрыгнула через ленивого пса
Если определено (как параметр функции), чтобы обрезать эту строку до 20 символов, результирующая строка должна выглядеть примерно так:
Быстрый коричневый … ленивый пес
Самая близкая реализация, которую я пришел, была:
function truncate( $string, $length, $append = NULL ) {
if( strlen( $string ) <= $length ) return $string;
$append = ( strlen( $append ) ? sprintf( ' %s ', $append ) : ' ... ' );
$start = round( $length / 2 );
$start = strlen( substr( $string, 0, ( strpos( substr( $string, $start ), ' ' ) + $start ) ) );
$end = ( $start - strlen( $append ) );
$end = strlen( substr( $string, 0, strrpos( substr( $string, $start + strlen( $append ) - 1 ), ' ' ) ) );
return substr( $string, 0, $start ) . $append . substr( $string, ( strlen( $string ) - $end ) );
}
Но не только это не работает гладко со строками разной длины, но также не усекается до заданного размера.
Для некоторых строк я получаю дублированные пустые символы (из-за неправильной математики относительно пробелов, используемых sprintf () над $ Append), иногда одна буква удаляется из слова, ближайшего к интерполированной строке, а иногда слово разрывается пополам, когда этого не должно быть.
Приведенная выше строка, например, если используется как:
truncate( $str, 20 );
Результаты в:
Быстрый коричневый … поглаживает ленивую собаку
Чтобы избежать усечения в середине слова, я сначала смотрю wordwrap()
так как у него уже есть эта способность по умолчанию.
Таким образом, подход, который я бы выбрал вместо этого, заключается в использовании wordwrap()
разделить строку на сегменты примерно вдвое меньше общей длины, минус длина строки-разделителя.
Затем объедините первую строку из wordwrap()
, разделитель и последняя строка. (Использование explode()
разделить wordwrap()
вывод в строки).
// 3 params: input $string, $total_length desired, $separator to use
function truncate($string, $total_length, $separator) {
// The wordwrap length is half the total minus the separator's length
// trim() is used to prevent surrounding space on $separator affecting the length
$len = ($total_length - strlen(trim($separator))) / 2;
// Separate the output from wordwrap() into an array of lines
$segments = explode("\n", wordwrap($string, $len));
// Return the first, separator, last
return reset($segments) . $separator . end($segments);
}
Попробуйте это: http://codepad.viper-7.com/ai6mAK
$s1 = "The quick brown fox jumped over the lazy dog";
$s2 = "Lorem ipsum dolor sit amet, nam id laudem aliquid. Option utroque interpretaris eu sea, pro ea illud alterum, sed consulatu conclusionemque ei. In alii diceret est. Alia oratio ei duo.";
$s3 = "This is some other long string that ought to get truncated and leave some stuff on the end of it.";
// Fox...
echo truncate($s1, 30, "...");
// Lorem ipsum...
echo truncate($s2, 30, "...");
// Other one
echo truncate($s3, 40, "...");
Выходы:
The quick...the lazy dog
Lorem ipsum...ei duo.
This is some...on the end of it.
Обратите внимание на этот вывод, что последний бит ei duo
немного короче. Это потому, что последняя строка wordwrap()
вернулся не из общей длины. Это можно обойти, если это важно для вас, проверяя strlen()
последнего элемента из $segments
массив и, если он меньше некоторого порога (скажем, $len / 2
) разбить элемент массива перед ним на слова explode()
и добавьте еще одно слово из этого массива.
Вот улучшенная версия, решающая эту проблему путем возврата ко второй-последней строке wordwrap()
и выталкивая слова, пока окончание не станет как минимум на половину длины $total_length
, Это немного сложнее, но имеет более удовлетворительный результат. http://codepad.viper-7.com/mDmlL0
function truncate($string, $total_length, $separator) {
// The wordwrap length is half the total, minus the separator's length
$len = (int)($total_length - strlen($separator)) / 2;
// Separate the output from wordwrap() into an array of lines
$segments = explode("\n", wordwrap($string, $len));
// Last element's length is less than half $len, append words from the second-last element
$end = end($segments);
// Add words from the second-last line until the end is at least
// half as long as $total_length
if (strlen($end) <= $total_length / 2 && count($segments) > 2) {
$prev = explode(' ', prev($segments));
while (strlen($end) <= $total_length / 2) {
$end = array_pop($prev) . ' ' . $end;
}
}
// Return the first, separator, last
return reset($segments) . $separator . $end;
}
// Produces:
The quick...over the lazy dog
Lorem ipsum...Alia oratio ei duo.
This is some other...stuff on the end of it.
Других решений пока нет …