如何将PHP中的字符串截断为最接近一定数量字符的单词?

时间:2008-09-17 04:24:04

标签: php string function

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

29 个答案:

答案 0 :(得分:212)

使用wordwrap功能。它将文本分成多行,使得最大宽度是您指定的宽度,在字边界处断开。拆分后,您只需要第一行:

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

这个oneliner无法处理的一件事是文本本身短于所需宽度的情况。要处理这种边缘情况,应该做类似的事情:

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'来处理它:

$parts = preg_split('/([\s\n\r]+)/u', $string, null, PREG_SPLIT_DELIM_CAPTURE);

答案 1 :(得分:131)

这将返回单词的前200个字符:

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

答案 2 :(得分:42)

$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

并且你有它 - 一种可靠的方法,可以将任何字符串截断为最接近的整个单词,同时保持在最大字符串长度之下。

我尝试了上面的其他例子,但没有产生预期的效果。

答案 3 :(得分:34)

当我注意到wordwrap函数的$ break参数时,以下解决方案诞生了:

  

string wordwrap(string $ str [,int $ width = 75 [,string $ 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.

答案 4 :(得分:9)

在任何地方,如果中文和日文等某些语言不使用空格字符来分割单词,请记住“单词”。此外,恶意用户可以简单地输入没有任何空格的文本,或者使用与标准空格字符类似的Unicode,在这种情况下,您使用的任何解决方案最终都可能最终显示整个文本。解决这个问题的方法可能是在正常情况下将其拆分为空格后检查字符串长度,然后,如果字符串仍然高于异常限制 - 在这种情况下可能是225个字符 - 继续并在该限制下愚蠢地将其拆分。

对于非ASCII字符,还有一个需要注意的事情;包含它们的字符串可能被PHP的标准strlen()解释为比它们实际上更长,因为单个字符可能需要两个或更多字节而不是一个字节。如果你只是使用strlen()/ substr()函数来分割字符串,你可以在字符的中间分割一个字符串!如有疑问,mb_strlen() / mb_substr()会更加万无一失。

答案 5 :(得分:8)

使用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个字符后第一个空格处截断的字符串。

答案 6 :(得分:5)

这是我的功能基于@ Cd-MaN的方法。

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

  return $string;
}

答案 7 :(得分:4)

你走了:

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

答案 8 :(得分:3)

令人惊讶的是找到这个问题的完美解决方案是多么棘手。我还没有在这个页面上找到一个在至少某些情况下不会失败的答案(特别是如果字符串包含换行符或制表符,或者单词分隔符不是空格,或者string有UTF-8多字节字符)。

这是一个适用于所有情况的简单解决方案。这里有类似的答案,但是&#34; s&#34;修改器很重要,如果你想让它用于多行输入,并且&#34; u&#34;修饰符使其正确评估UTF-8多字节字符。

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

一个可能的边缘情况......如果字符串在前$ characterCount字符中根本没有任何空格,它将返回整个字符串。如果你喜欢它会强制在$ characterCount中断,即使它不是一个单词边界,你可以使用它:

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

最后一个选项,如果你想让它添加省略号,如果它截断字符串......

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

答案 9 :(得分:3)

$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.com orrrr正好200个字符。
  3. 排除第五个r orrrrr之后
  4. regex101.com
  5. 享受。

答案 10 :(得分:2)

我会使用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);

答案 11 :(得分:2)

好的,所以我根据上面的答案得到了另一个版本,但考虑了更多的东西(utf-8,\ n和&amp; nbsp;),如果与wp一起使用,还会删除评论的wordpress短代码。< / p>

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

答案 12 :(得分:2)

/*
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个与我们相关。

第一个参数 - 模式 第一个参数是正则表达式模式,字符串将沿其分割。在我们的例子中,我们希望将字符串分割为字边界。因此,我们使用预定义的字符类\s,它匹配空格,制表符,回车符和换行符等空格字符。

第二个参数 - 输入字符串 第二个参数是我们要拆分的长文本字符串。

第三个参数 - 限制 第三个参数指定应返回的子字符串数。如果将限制设置为n,则preg_split将返回n个元素的数组。第一个n-1元素将包含子字符串。最后一个(n th)元素将包含字符串的其余部分。

答案 13 :(得分:1)

你可以使用这个:

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]

答案 14 :(得分:1)

虽然这是一个相当老的问题,但我想我可以提供一个替代方法,因为它没有被提及并且对PHP 4.3+有效。

您可以使用%.ℕs精度修饰符来使用sprintf系列函数来截断文本。

句点.后跟一个整数,其含义取决于 说明符:

  • 对于e,E,f和F指示符:这是小数点后要打印的位数(默认为6)。
  • 对于g和G指定符:这是要打印的最大有效位数。
  • 对于s声明符:它是一个截止点,为字符串设置了最大字符数限制

简单截断https://3v4l.org/QJDJU

$string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var_dump(sprintf('%.10s', $string));

结果

string(10) "0123456789"

扩展的截断https://3v4l.org/FCD21

由于sprintf的功能类似于substr,因此会部分切断单词。下面的方法将通过使用带有特殊定界符的strpos(wordwrap(..., '[break]'), '[break]')来确保单词不会被截断。这使我们能够检索位置并确保我们在标准句子结构上不匹配。

在不截断单词且不超过指定宽度的情况下返回字符串,同时根据需要保留换行符。

function truncate($string, $width, $on = '[break]') {
    if (strlen($string) > $width && false !== ($p = strpos(wordwrap($string, $width, $on), $on))) {
        $string = sprintf('%.'. $p . 's', $string);
    }
    return $string;
}
var_dump(truncate('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', 20));

var_dump(truncate("Lorem Ipsum is simply dummy text of the printing and typesetting industry.", 20));

var_dump(truncate("Lorem Ipsum\nis simply dummy text of the printing and typesetting industry.", 20));

结果

/* 
string(36) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"  
string(14) "Lorem Ipsum is" 
string(14) "Lorem Ipsum
is" 
*/

使用wordwrap($string, $width)strtok(wordwrap($string, $width), "\n")的结果

/*
string(14) "Lorem Ipsum is"
string(11) "Lorem Ipsum"
*/

答案 15 :(得分:1)

我就这样做了:

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

答案 16 :(得分:1)

我的功能几乎可以满足您的需求,如果您进行一些编辑,它将完全适合:

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

答案 17 :(得分:1)

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

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

唯一的区别是在$ string的末尾添加一个空格。这确保根据ReX357的评论不会切断最后一个单词。

我没有足够的代表点将其添加为评论。

答案 18 :(得分:1)

基于@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]. '...';
}

答案 19 :(得分:0)

我之前用过

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

答案 20 :(得分:0)

我创建了一个更类似于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。:全长切割可能小于substr。

答案 21 :(得分:0)

我知道这是旧的,但是......

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

答案 22 :(得分:0)

将IF / ELSEIF语句添加到DaveAmalMurali的代码中,以处理没有空格的字符串

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

答案 23 :(得分:0)

我相信这是最简单的方法:

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

我使用特殊字符分割文字并将其剪切。

答案 24 :(得分:0)

我觉得这很有效:

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

}

缓冲区允许您调整返回字符串的长度。

答案 25 :(得分:0)

使用此:

以下代码将删除“,”。如果您还有其他字符或子字符串,则可以使用它代替','

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

//如果您还有另一个字符串帐户

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

答案 26 :(得分:0)

// a looonnng string ...
$str = "Le Lorem Ipsum est simplement du 
faux texte employé dans la composition et 
la mise en page avant impression. 
Le Lorem Ipsum est le faux texte standard de 
l'imprimerie depuis les années 1500, quand un 
imprimeur anonyme assembla ensemble des morceaux 
de texte pour réaliser un livre spécimen de polices
de texte. Il n'a pas fait que survivre cinq siècles,
mais s'est aussi adapté à la bureautique informatique,
sans que son contenu n'en soit modifié. Il a été 
popularisé dans les années 1960 grâce à la vente 
de feuilles Letraset contenant des passages du
Lorem Ipsum, et, plus récemment, par son inclusion 
dans des applications de mise en page de texte, 
comme Aldus PageMaker";
// number chars to cut
$number_to_cut = 300;
// string truncated in one line !
$truncated_string = 
substr($str, 0, strrpos(substr($str, 0, $number_to_cut), ' '));
// test return
echo $truncated_string;

// variation (add ellipsis) : echo $truncated_string.' ...';

// output :
/* Le Lorem Ipsum est simplement du 
faux texte employé dans la composition et 
la mise en page avant impression. 
Le Lorem Ipsum est le faux texte standard de 
l'imprimerie depuis les années 1500, quand un 
imprimeur anonyme assembla ensemble des morceaux 
de texte pour réaliser un livre
*/

答案 27 :(得分:-1)

在这里你可以尝试这个

substr( $str, 0, strpos($str, ' ', 200) ); 

答案 28 :(得分:-2)

这可能对某人有所帮助:

<?php

    $string = "Your line of text";
    $spl = preg_match("/([, \.\d\-''\"\"_()]*\w+[, \.\d\-''\"\"_()]*){50}/", $string, $matches);
    if (isset($matches[0])) {
        $matches[0] .= "...";
        echo "<br />" . $matches[0];
    } else {
        echo "<br />" . $string;
    }

?>