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

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

通过使用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'来处理它:

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

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

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

在这里你可以得到一个可靠的方法,将字符串截断成最接近的整个单词,同时保持最大的字符串长度。

我已经尝试了上面的其他例子,他们没有产生预期的结果。

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

字符串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. 

请记住,无论何时,有些语言(如中文和日语)不使用空格字符来拆分单词,而是通过“单词”进行拆分。 此外,恶意用户可以简单地输入文本而不用任何空格,或者使用与标准空格字符相似的Unicode,在这种情况下,您使用的任何解决方案都可能最终显示整个文本。 解决这个问题的方法可能是检查字符串的长度,然后如果字符串仍然超出了一个异常的限制(在这种情况下可能是225个字符),然后在这个限制之下将其分开。

当涉及到非ASCII字符时,还要注意这个问题。 包含它们的字符串可能被PHP的标准strlen()解释为比实际更长,因为单个字符可能需要两个或多个字节而不是一个字节。 如果您只是使用strlen()/ substr()函数来拆分字符串,则可以在字符中间拆分字符串! 如果有疑问, mb_strlen() / mb_substr()会更简单一些。

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

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

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

干得好:

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

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

好的,所以我得到了另一个版本的基础上面的答案,但考虑更多的事情(UTF-8,\ n和&nbsp;),也剥去了wordpress短代码评论如果使用wp。

 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; } 
 /* 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)元素将包含字符串的其余部分。

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

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

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

唯一的区别是在$ string的末尾添加一个空格。 这可以确保根据ReX357的评论,最后一个字不会被截断。

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

我有一个几乎可以实现你想要的功能,如果你做了一些编辑,它将完全适合:

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

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

这是一个简单的解决方案,适用于所有情况。 在这里也有类似的答案,但是如果你想要使用“s”修饰符来处理多行输入,而“u”修饰符能正确评估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; } 
 $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. 排除第五个regex101.com之后的orrrrr

请享用。

我知道这是旧的,但…

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

我以前用过这个

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

我创建了一个更类似于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。

在Dave和AmalMurali的代码中添加IF / ELSEIF语句来处理没有空格的字符串

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

这是我做到的:

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

在这里你可以试试这个

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

可能是这样会帮助某人:

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