it-swarm-pt.tech

Como truncar uma string em PHP para o Word mais próximo de um certo número de caracteres?

Eu tenho um trecho de código escrito em PHP que puxa um bloco de texto de um banco de dados e envia para um widget em uma página da web. O bloco original de texto pode ser um artigo longo ou uma frase curta ou duas; mas, para esse widget, não posso exibir mais do que, digamos, 200 caracteres. Eu poderia usar substr () para cortar o texto em 200 caracteres, mas o resultado seria cortar no meio de palavras - o que eu realmente quero é cortar o texto no final do último Palavra antes de 200 caracteres.

170
Brian

Usando a função wordwrap . Ele divide os textos em várias linhas, de modo que a largura máxima é aquela especificada, quebrando nos limites do Word. Após a divisão, você simplesmente pega a primeira linha:

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

Uma coisa que este oneliner não manipula é o caso quando o próprio texto é menor que a largura desejada. Para lidar com este caso Edge, deve-se fazer algo como:

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

A solução acima tem o problema de cortar prematuramente o texto se ele contiver uma nova linha antes do ponto de corte real. Aqui uma versão que resolve esse problema:

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

Além disso, aqui está a classe de teste PHPUnit usada para testar a implementação:

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

EDIT:

Caracteres UTF8 especiais, como 'à', não são manipulados. Adicione 'u' no final do REGEX para lidar com isso:

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

212
Grey Panther

Isso retornará os primeiros 200 caracteres de palavras:

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

E aí está - um método confiável de truncar qualquer string para o Word inteiro mais próximo, mantendo-se sob o comprimento máximo da string.

Eu tentei os outros exemplos acima e eles não produziram os resultados desejados.

42
Dave

A seguinte solução nasceu quando notei um parâmetro $ break de wordwrap function:

string wordwrap (string $ str [ int $ width = 75 [ string $ break = "\ n" [ bool $ cut = false]]])

Aqui está a solução:

/**
 * 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");
}

Exemplo 1.

print truncate("This is very long string with many chars.", 25);

O exemplo acima irá gerar:

This is very long string...

Exemplo # 2.

print truncate("This is short string.", 25);

O exemplo acima irá gerar:

This is short string.
33
Sergiy Sokolenko

Tenha em mente sempre que você está dividindo por "Word" em qualquer lugar que alguns idiomas, como chinês e japonês, não usam um caractere de espaço para dividir as palavras. Além disso, um usuário mal-intencionado pode simplesmente inserir texto sem espaços ou usar alguma aparência semelhante a Unicode para o caractere de espaço padrão. Nesse caso, qualquer solução usada pode acabar exibindo o texto inteiro de qualquer maneira. Uma maneira de contornar isso pode ser verificar o tamanho da string depois de dividi-la nos espaços normalmente, então, se a string ainda estiver acima de um limite anormal - talvez 225 caracteres neste caso -, vá em frente e divida-a nesse limite.

Mais uma ressalva com coisas como esta quando se trata de caracteres não-ASCII; strings que os contêm podem ser interpretados pelo strlen () padrão do PHP como sendo mais longos do que realmente são, porque um único caractere pode ter dois ou mais bytes em vez de apenas um. Se você acabou de usar as funções strlen ()/substr () para dividir as strings, você pode dividir uma string no meio de um caractere! Quando em dúvida, mb_strlen () / mb_substr () são um pouco mais infalíveis.

9
Garrett Albright

Use strpos e 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;

Isto lhe dará uma string truncada no primeiro espaço após 30 caracteres.

8
Lucas Oman

Aqui está minha função baseada na abordagem do @ 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

Aqui está:

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

É surpreendente como é complicado encontrar a solução perfeita para esse problema. Eu ainda não encontrei uma resposta nesta página que não falha em pelo menos algumas situações (especialmente se a string contém novas linhas ou abas, ou se a quebra do Word é diferente de um espaço, ou se a string tem UTF- 8 caracteres multibyte).

Aqui está uma solução simples que funciona em todos os casos. Houve respostas semelhantes aqui, mas o modificador "s" é importante se você quiser que ele funcione com entradas de várias linhas, e o modificador "u" faz com que ele avalie corretamente os caracteres de multibyte UTF-8.

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

Um possível caso Edge com este ... se a string não tiver nenhum espaço em branco nos primeiros caracteres $ characterCount, ele retornará toda a string. Se você preferir, força uma quebra em $ characterCount mesmo que não seja um limite do Word, você pode usar isto:

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

Uma última opção, se você quiser que ele adicione Ellipsis se truncar a string ... 

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

Descrição:

  • ^ - começa do começo da string
  • ([\s\S]{1,200}) - recebe de 1 a 200 de qualquer caractere
  • [\s]+? - não inclui espaços no final do texto curto para que possamos evitar Word ... em vez de Word...
  • [\s\S]+ - corresponde a todos os outros conteúdos

Testes:

  1. regex101.com vamos adicionar ao or alguns outros r
  2. regex101.comorrrr exatamente 200 caracteres.
  3. regex101.com after fifth rorrrrr excluded.

Apreciar.

3
hlcs

Ok, então eu tenho outra versão deste baseado nas respostas acima, mas tendo mais coisas em conta (utf-8,\n e & nbsp;), também uma linha tirando os códigos de acesso wordpress comentados se usado com 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;
  }
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);
}

Uso:

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

Isto produzirá as primeiras 10 palavras.

A função preg_split é usada para dividir uma string em substrings. Os limites ao longo dos quais a string deve ser dividida são especificados usando um padrão de expressões regulares.

preg_split função leva 4 parâmetros, mas apenas os 3 primeiros são relevantes para nós agora.

First Parameter - Pattern O primeiro parâmetro é o padrão de expressões regulares ao longo do qual a string deve ser dividida. No nosso caso, queremos dividir a string nos limites do Word. Portanto, usamos uma classe de caractere predefinida \s, que corresponde a caracteres de espaço em branco, como espaço, tabulação, retorno de carro e alimentação de linha.

Segundo Parâmetro - String de Entrada O segundo parâmetro é a string de texto longo que queremos dividir.

Terceiro Parâmetro - Limite O terceiro parâmetro especifica o número de substrings que devem ser retornados. Se você definir o limite para n, preg_split retornará uma matriz de n elementos. Os primeiros elementos n-1 conterão as substrings. O último elemento (n th) conterá o resto da string.

2
bodi0

Eu usaria a função preg_match para fazer isso, pois o que você quer é uma expressão bem simples.

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

A expressão significa "corresponde a qualquer substring a partir do início do comprimento 1-200 que termina com um espaço". O resultado é em $ result e a partida é em $ matches. Isso cuida da sua pergunta original, que está especificamente terminando em qualquer espaço. Se você quiser que isso termine em novas linhas, altere a expressão regular para:

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

É assim que eu fiz:

$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

Eu tenho uma função que faz quase o que você quer, se você fizer algumas edições, ela se encaixará exatamente:

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

Baseado no regex de @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

Esta é uma pequena correção para a resposta da mattmac:

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

A única diferença é adicionar um espaço no final de $ string. Isso garante que o último Word não seja cortado de acordo com o comentário de ReX357.

Eu não tenho pontos de repetição suficientes para adicionar isso como um comentário.

1
tanc

Adicionadas instruções IF/ELSEIF ao código de Dave e AmalMurali para manipular cadeias sem espaços

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

Eu sei que isso é velho, mas ...

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

Eu crio uma função mais semelhante ao substr e usando a idéia do @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 .: O corte de comprimento total pode ser menor que substr.

0
evandro777

Eu acredito que esta é a maneira mais fácil de fazer isso:

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

Eu estou usando os caracteres especiais para dividir o texto e cortá-lo.

0
Namida

Eu usei isso antes

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

Eu acho que isso funciona:

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;

}

O buffer permite que você ajuste o comprimento da string retornada.

0
Mat Barnett

Usa isto: 

o seguinte código irá remover ','. Se você tem outro caractere ou sub-string, você pode usar isso em vez de ','

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

// se você tiver outra conta de string para 

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