Como Truncar uma seqüência de caracteres em PHP para a palavra mais próxima para um determinado número de caracteres?

StackOverflow https://stackoverflow.com/questions/79960

  •  09-06-2019
  •  | 
  •  

Pergunta

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

Foi útil?

Solução

Usando o de quebra automática de linha função.Ele divide os textos em várias linhas de tal forma que a largura máxima é o especificado, quebra os limites da palavra.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 pode tratar é o caso quando o próprio texto é menor do que a largura desejada.Para lidar com essa borda-caso, 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 da prematuramente para cortar o texto se ele contém uma quebra de linha antes de o real cutpoint.Aqui uma versão que resolve este 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));
}

Também, aqui é o PHPUnit testclass usado 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));
  }
}

EDITAR :

Especial de caracteres UTF8 como 'à' não são tratados.Adicionar 'u' no final do REGEX para lidar com isso:

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

Outras dicas

Isto irá devolver os 200 primeiros caracteres de palavras:

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

E aí, um método confiável de truncar qualquer seqüência de caracteres para o inteiro mais próximo palavra, enquanto permanecer sob a máxima de seqüência de caracteres de comprimento.

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

A seguinte solução nasceu quando eu notei um $quebra de parâmetro de de quebra automática de linha função:

seqüência de caracteres de quebra automática de linha ( string $str [, int $largura = 75 [, string $break = " " [, bool $cortar = false ]]] )

Aqui é 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á saída:

This is very long string...

Exemplo #2.

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

O exemplo acima irá saída:

This is short string.

Tenha em mente sempre que você está partindo por "palavra" em qualquer lugar que alguns idiomas, como o Chinês e o Japonês não use um caractere de espaço para separar palavras.Além disso, um usuário mal-intencionado poderia simplesmente introduza o texto, sem espaços, ou usando Unicode alguns sósia para o padrão de caractere de espaço, neste caso, qualquer solução que você usar podem acabar exibir todo o texto de qualquer maneira.Uma maneira de contornar isso pode ser para verificar o comprimento de cadeia depois que a divisão é em espaços como o normal, então, se a cadeia é ainda acima de um anormal limite - talvez 225 caracteres (neste caso, vai à frente e dividir dumbly nesse limite.

Uma advertência mais com coisas como esta quando se trata de caracteres não-ASCII;cadeias com eles pode ser interpretado pelo PHP padrão strlen() como sendo mais do que eles realmente são, porque um único caractere pode levar dois ou mais bytes em vez de apenas um.Se você apenas usar o strlen()/substr() funções para dividir seqüências de caracteres, você pode dividir uma seqüência de caracteres no meio de um personagem!Quando em dúvida, mb_strlen()/mb_substr() são um pouco mais à prova de falhas.

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 irá dar-lhe uma seqüência de caracteres truncados no primeiro espaço, depois de 30 caracteres.

Aqui é minha função com base no @Cd-Homem abordagem.

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

  return $string;
}

Aqui você vai:

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

É 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 seqüência de caracteres contém quebras de linha ou guias, ou se a palavra break é outra coisa do que um espaço, ou se a seqüência de caracteres tem UTF-8 caracteres multibyte).

Aqui está uma solução simples que funciona em todos os casos.Houve respostas semelhantes aqui, mas o "s" modificador é importante se você quer trabalhar com multi-entrada de linha, e o "u" modificador torna avaliar correctamente a codificação UTF-8 caracteres multibyte.

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

Um possível caso de borda com isso...se a cadeia não tem nenhum espaço em branco no primeiro $characterCount caracteres, ele irá retornar a string inteira.Se você preferir, força uma quebra de us $characterCount mesmo se ele não é um limite de palavra, 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 tê-lo adicionar reticências se trunca a cadeia...

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

Descrição:

  • ^ - iniciar a partir do início da seqüência de caracteres
  • ([\s\S]{1,200}) - começar a partir de 1 200 de qualquer personagem
  • [\s]+? - não incluir espaços no final do curto texto para que possamos evitar word ... em vez de word...
  • [\s\S]+ - partida todos os outros conteúdos

Testes:

  1. regex101.com vamos adicionar para or alguns outros r
  2. regex101.com orrrr exatamente 200 caracteres.
  3. regex101.com após o quinto r orrrrr excluídos.

Desfrute.

Gostaria de usar a função preg_match para fazer isso, conforme o que você deseja é muito simples expressão.

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

A expressão significa "combinar com qualquer subseqüência de caracteres a partir do início da duração de 1 a 200, que termina com um espaço." O resultado é de $resultado, e o jogo é em $partidas.Que cuida de sua pergunta original, que é especificamente terminando em qualquer espaço.Se você quer fazer final no quebras de linha, alterar a expressão regular para:

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

Ok, então eu tenho outra versão deste baseado nas respostas acima, mas tendo coisas mais em conta(utf-8 e ;), também uma linha de retirada os shortcodes do wordpress comentou se usado com o 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);
}

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

Esta será a saída primeiras 10 palavras.

O preg_split a função é usada para dividir uma string em substrings.Os limites ao longo do qual a seqüência de caracteres é para ser dividido, são especificados utilizando expressões regulares padrão.

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

Primeiro Parâmetro – Padrão O primeiro parâmetro é o expressões regulares padrão ao longo do qual a seqüência de caracteres é para ser dividido.No nosso caso, queremos dividir a seqüência de caracteres em todo limites da palavra.Portanto, usamos uma classe de caracteres predefinidos \s que corresponde caracteres de espaço em branco (espaço, tab, retorno de carro e alimentação de linha.

O Segundo Parâmetro De Entrada De Seqüência De Caracteres O segundo parâmetro é a longa seqüência de caracteres de texto que queremos dividir.

Terceiro Parâmetro De Limite De O terceiro parâmetro especifica o número de textos que devem ser devolvidos.Se você definir o limite para n, preg_split irá retornar um array de n elementos.O primeiro n-1 elementos contém as subseqüências.O último (n th) elemento irá conter o restante da seqüência de caracteres.

Com base no @Justin Poliey do regex:

// 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]. '...';
}

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

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

A única diferença é que para adicionar um espaço no final de $cadeia.Isto assegura que a última palavra não é cortada, como por ReX357 comentário.

Eu não tenho o suficiente de pontos de rep adicionar isto como um comentário.

Eu tenho uma função que faz quase o que você quer, se você vai fazer algumas edições, ele vai 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;
}
?>

Isso é como 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")));

Eu sei que isso é antigo, mas...

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

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

Vou criar uma função mais semelhante ao substr, e usando a idéia de Porra.

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 comprimento total de corte pode ser menor do que substr.

Adicionado IF/ELSEIF instruções para o código de Dave e AmalMurali para a manipulação de cadeias de caracteres 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);
}

Eu acredito que esta é a forma mais fácil de o fazer:

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

Eu estou usando os caracteres especiais para dividir o texto e corte.

Eu acho isso funciona:

função 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 cadeia de caracteres retornada.

Use isto:

o código a seguir irá remover ','.Se você tiver uns outros caracteres ou sub-cadeia de caracteres, você pode usar isso em vez de ','

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

// se você tem outra cadeia de conta para

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

Aqui você pode tentar este

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

Pode ser que isto irá ajudar alguém:

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

?>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top