截断长字符串,不用点插值打破单词

时间:2014-12-18 16:28:15

标签: php string-interpolation

我正在尝试将长字符串截断为特定数量的字符,并在其中间插入另一个用户定义的字符串(或多或少)以表示该字符串已被截断。与此同时,我正试图让这些话不要被打破。 E.g:

快速的棕色狐狸跳过懒狗

如果定义(作为函数参数)将此字符串截断为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 );

结果:

  

快速的棕色......趴在懒狗身上

1 个答案:

答案 0 :(得分:2)

为避免中间词截断,我首先查看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.