【问题标题】:Truncate long strings, without break the words with dots interpolation截断长字符串,不使用点插值打断单词
【发布时间】:2014-12-18 16:28:14
【问题描述】:

我正在尝试将长字符串截断为特定数量的字符,并在其中插入另一个用户定义的字符串(或多或少)以表示该字符串已被截断。同时,我试图让这些词不被分成两半。例如:

敏捷的棕狐跳过了懒狗

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

结果:

快速的棕色...在懒惰的狗身上踩过

【问题讨论】:

  • 长度 20 适合哪里?您的示例字符串输出实际上是 32,所以在 20 时它真的是 "The quick...lazy dog" 吗?

标签: php string-interpolation


【解决方案1】:

为了避免中间词被截断,我首先查看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() 不是总长度。如果它对您很重要,可以通过检查$segments 数组中最后一个元素的strlen() 并且如果它小于某个阈值(比如$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.

【讨论】:

  • 伙计...当我在等待答案时,我再次尝试,我非常接近你的方法。我错过了 $len 的正确逻辑。就个人而言,现在,我不需要字符串的长度尽可能接近定义的长度,因此,如果您不介意,我希望看到一种更扩展的方法来实现这一点。另外,我注意到的一件事是,如果 $separator 有边界空间,输出会变短。
  • @BrunoAugusto 你的意思是你希望看到最后一段更好地充实以接近预期的长度?要解决分隔符周围的空格问题,请使用 strlen(trim($separator)) 而不是 strlen($separator)
  • 这就是我想要的。只是出于好奇,我希望我没有在辱骂你,但是对于分隔符的字符数多于在它周围添加的子字符串?
  • @BrunoAugusto 好的,这个版本管理的字数最少。它需要一些技巧才能让它真正完美,但我现在没有时间做更多的工作。我不打算在上面编辑这个版本,因为它需要改进。但希望这足以让你走上自己的道路。 codepad.viper-7.com/WEXMlf
  • 没问题,你已经帮了我很多了。我可以看到,这样的小事对程序来说会付出更多的代价。
猜你喜欢
  • 2011-10-02
  • 2021-10-28
  • 2019-01-03
  • 2015-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-04
  • 1970-01-01
相关资源
最近更新 更多