我有一个用PHP编写的代码片段,它从数据库中提取一个文本块,并将其发送到网页上的一个小部件。原文可以是一篇很长的文章,也可以是一两个短句;但是对于这个小部件,我不能显示超过200个字符。我可以使用substr()在200个字符处切断文本,但结果将在单词中间切断——我真正想要的是在200个字符前的最后一个单词的末尾切断文本。


当前回答

我以前用过这个

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

其他回答

你可以用这个:

function word_shortener($text, $words=10, $sp='...'){

  $all = explode(' ', $text);
  $str = '';
  $count = 1;

  foreach($all as $key){
    $str .= $key . ($count >= $words ? '' : ' ');
    $count++;
    if($count > $words){
      break;
    }
  }

  return $str . (count($all) <= $words ? '' : $sp);

}

例子:

word_shortener("Hello world, this is a text", 3); // Hello world, this...
word_shortener("Hello world, this is a text", 3, ''); // Hello world, this
word_shortener("Hello world, this is a text", 3, '[read more]'); // Hello world, this[read more]

Edit

工作原理:

1. 从输入文本中打开空格:

$all = explode(' ', $text);

例如,如果$text将是"Hello world",那么$all是一个带有爆炸值的数组:

“你好”,“世界”)

2. 对于每个单词:

选择展开文本中的每个元素:

foreach($all as $key){...

将当前单词($key)附加到$str,如果它是最后一个单词,则添加空格:

$str .= $key . ($count >= $words ? '' : ' ');

然后给$count加上1,检查它是否大于max limit($words)打破循环:

if($count > $words){
   break;
}

只有当最终文本小于输入文本时,才返回$str和分隔符($sp):

return $str . (count($all) <= $words ? '' : $sp);

这是一个小修复mattmac的答案:

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

唯一的区别是在$string的末尾添加一个空格。这确保了最后一个词不会像ReX357的注释那样被切断。

我没有足够的代表点添加这作为一个评论。

通过使用wordwrap函数。它将文本分割成多行,这样最大宽度就是您指定的宽度,在单词边界处断开。分割之后,你只需取第一行:

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

这个线性程序不能处理的一件事是,当文本本身比所需的宽度短时。为了处理这种边情况,我们应该这样做:

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字符不会被处理。在REGEX的末尾添加'u'来处理它:

部分美元= preg_split (' / ([\ s \ n \ r] +) / u ',美元字符串,null, PREG_SPLIT_DELIM_CAPTURE);

我将使用preg_match函数来做到这一点,因为您想要的是一个相当简单的表达式。

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

表达式的意思是“匹配从长度1-200开始以空格结尾的任何子字符串”。结果在$result中,匹配在$matches中。这就解决了你最初的问题,即以任意空格结束的问题。如果你想让它以换行符结束,将正则表达式更改为:

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

Keep in mind whenever you're splitting by "word" anywhere that some languages such as Chinese and Japanese do not use a space character to split words. Also, a malicious user could simply enter text without any spaces, or using some Unicode look-alike to the standard space character, in which case any solution you use may end up displaying the entire text anyway. A way around this may be to check the string length after splitting it on spaces as normal, then, if the string is still above an abnormal limit - maybe 225 characters in this case - going ahead and splitting it dumbly at that limit.

当涉及到非ascii字符时,还有一个类似的警告;包含它们的字符串可能会被PHP的标准strlen()解释为比实际更长,因为单个字符可能占用两个或更多字节,而不是一个字节。如果你只是使用strlen()/substr()函数来分割字符串,你可能会在字符中间分割字符串!如果有疑问,mb_strlen()/mb_substr()更简单一些。