it-swarm.cn

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

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

171
Brian

通过使用 wordwrap 函数。它将文本分成多行,使得最大宽度是您指定的宽度,在Word边界处断开。拆分后,您只需占用第一行:

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

这个oneliner无法处理的一件事是文本本身短于所需宽度的情况。要处理此Edge-case,应该执行以下操作:

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

212
Grey Panther

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

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

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

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

42
Dave

当我注意到 wordwrap function的$ 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.
34
Sergiy Sokolenko

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

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

9
Garrett Albright

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

8
Lucas Oman

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

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

  return $string;
}
5
Camsoft

干得好:

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

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

这是一个适用于所有情况的简单解决方案。这里有类似的答案,但如果您希望它与多行输入一起使用,则“s”修饰符很重要,而“u”修饰符使其正确评估UTF-8多字节字符。

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

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

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;
}
3
orrd
$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.comorrrr正好是200个字符。
  3. regex101.com 排除第五个rorrrrr之后。

请享用。

3
hlcs

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

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;
  }
2
Yo-L
/*
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个与我们相关。

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

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

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

2
bodi0

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

这就是我做到的方式:

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

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

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

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

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

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

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

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

1
tanc

将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);
}
0
jdorenbush

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

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

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

0
evandro777

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

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

我正在使用特殊字符来分割文本并剪切它。

0
Namida

我之前用过这个

<?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;
?>
0
Yousef Altaf

我觉得这很有效:

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;

}

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

0
Mat Barnett

用这个:

以下代码将删除','。如果您有任何其他字符或子字符串,您可以使用它而不是','

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

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

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