it-swarm-es.tech

Cómo truncar una cadena en PHP a la palabra más cercana a un cierto número de caracteres?

Tengo un fragmento de código escrito en PHP que extrae un bloque de texto de una base de datos y lo envía a un widget en una página web. El bloque de texto original puede ser un artículo largo o una oración corta o dos; pero para este widget no puedo mostrar más de, por ejemplo, 200 caracteres. Podría usar substr () para cortar el texto a 200 caracteres, pero el resultado sería un corte en medio de las palabras: lo que realmente quiero es cortar el texto al final del último Palabra Antes de 200 caracteres.

171
Brian

Usando la función wordwrap . Divide los textos en varias líneas, de modo que el ancho máximo es el que usted especificó, rompiendo los límites de Word. Después de dividir, simplemente tome la primera línea:

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

Una cosa que este oneliner no maneja es el caso cuando el texto en sí es más corto que el ancho deseado. Para manejar este caso Edge, uno debería hacer algo como:

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

La solución anterior tiene el problema de cortar prematuramente el texto si contiene una nueva línea antes del punto de corte real. Aquí una versión que resuelve 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));
}

Además, aquí está la clase de prueba PHPUnit utilizada para probar la implementación:

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:

Los caracteres UTF8 especiales como 'à' no se manejan. Agregue 'u' al final del REGEX para manejarlo:

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

212
Grey Panther

Esto devolverá los primeros 200 caracteres de las palabras:

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

Y ahí lo tienen: un método confiable para truncar cualquier cadena a la Palabra completa más cercana, mientras se mantiene bajo la longitud máxima de la cadena.

He probado los otros ejemplos anteriores y no produjeron los resultados deseados.

42
Dave

La siguiente solución nació cuando noté un parámetro $ break de wordwrap function:

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

Aquí está la solución :

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

Ejemplo # 1.

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

El ejemplo anterior dará salida:

This is very long string...

Ejemplo # 2.

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

El ejemplo anterior dará salida:

This is short string.
34
Sergiy Sokolenko

Tenga en cuenta que cuando divida por "Palabra" en cualquier lugar, algunos idiomas, como el chino y el japonés, no usan un carácter de espacio para dividir palabras. Además, un usuario malintencionado podría simplemente ingresar texto sin espacios, o usar un aspecto Unicode similar al carácter de espacio estándar, en cuyo caso, cualquier solución que use podría terminar mostrando el texto completo de todos modos. Una forma de evitar esto puede ser verificar la longitud de la cadena después de dividirla en espacios como es normal, luego, si la cadena aún está por encima de un límite anormal, tal vez 225 caracteres en este caso, siga adelante y divídala en ese límite.

Una advertencia más con cosas como esta cuando se trata de caracteres no ASCII; las cadenas que los contienen pueden interpretarse por el estándar de PHP strlen () como más largas de lo que realmente son, porque un solo carácter puede tomar dos o más bytes en lugar de uno solo. Si solo usas las funciones strlen ()/substr () para dividir cadenas, ¡puedes dividir una cadena en medio de un personaje! En caso de duda, mb_strlen () / mb_substr () es un poco más infalible.

9
Garrett Albright

Utilice strpos y 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;

Esto le dará una cadena truncada en el primer espacio después de 30 caracteres.

8
Lucas Oman

Aquí está mi función basada en el enfoque de @ 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 tienes:

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

Es sorprendente lo difícil que es encontrar la solución perfecta para este problema. Todavía no he encontrado una respuesta en esta página que no falla al menos en algunas situaciones (especialmente si la cadena contiene nuevas líneas o pestañas, o si el salto de palabra no es un espacio, o si la cadena tiene UTF) 8 caracteres multibyte).

Aquí hay una solución simple que funciona en todos los casos. Hubo respuestas similares aquí, pero el modificador "s" es importante si desea que funcione con entradas de varias líneas, y el modificador "u" hace que evalúe correctamente los caracteres de varios bytes UTF-8.

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

Un posible caso de Edge con esto ... si la cadena no tiene ningún espacio en blanco en los primeros caracteres de $ characterCount, devolverá la cadena completa. Si prefieres forzar un descanso en $ characterCount, incluso si no es un límite de Word, puedes usar esto:

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

Una última opción, si desea que agregue Ellipsis si trunca la cadena ...

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

Descripción:

  • ^ - comienza desde el principio de la cadena
  • ([\s\S]{1,200}) - obtén de 1 a 200 de cualquier personaje
  • [\s]+? - no incluye espacios al final del texto breve, por lo que podemos evitar Word ... en lugar de Word...
  • [\s\S]+ - coincide con el resto del contenido

Pruebas:

  1. regex101.com vamos a agregar a or algunos otros r
  2. regex101.comorrrr exactamente 200 caracteres.
  3. regex101.com después de quinto rorrrrr excluido.

Disfrutar.

3
hlcs

Ok, tengo otra versión de esto basada en las respuestas anteriores pero teniendo en cuenta más cosas (utf-8,\n y & nbsp;), también una línea que elimina los códigos cortos de wordpress comentados si se usa con 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); 

Esto dará salida a las primeras 10 palabras.

La función preg_split se usa para dividir una cadena en subcadenas. Los límites a lo largo de los cuales se va a dividir la cadena, se especifican utilizando un patrón de expresiones regulares.

La función preg_split toma 4 parámetros, pero solo los 3 primeros son relevantes para nosotros en este momento.

Primer parámetro - Patrón El primer parámetro es el patrón de expresiones regulares a lo largo del cual se dividirá la cadena. En nuestro caso, queremos dividir la cadena a través de los límites de Word. Por lo tanto, utilizamos una clase de caracteres predefinida \s que coincide con los caracteres de espacio en blanco como el espacio, la pestaña, el retorno de carro y el salto de línea.

Segundo parámetro - Cadena de entrada El segundo parámetro es la cadena de texto larga que queremos dividir.

Tercer parámetro: límite El tercer parámetro especifica el número de subcadenas que deben devolverse. Si establece el límite en n, preg_split devolverá una matriz de n elementos. Los primeros elementos n-1 contendrán las subcadenas. El último elemento (n th) contendrá el resto de la cadena.

2
bodi0

Yo usaría la función preg_match para hacer esto, ya que lo que quieres es una expresión bastante simple.

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

La expresión significa "coincide con cualquier subcadena que comience desde el principio de la longitud 1-200 que termina con un espacio". El resultado está en $ resultado, y la coincidencia está en $ coincidencias. Eso se ocupa de su pregunta original, que está terminando específicamente en cualquier espacio. Si desea que finalice en nuevas líneas, cambie la expresión regular a:

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

Así es como lo hice:

$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

Tengo una función que hace casi lo que quieres, si haces algunas ediciones, se ajustará exactamente:

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

Basado en la expresión regular 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 es una pequeña solución para la respuesta de mattmac:

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

La única diferencia es agregar un espacio al final de $ cadena. Esto asegura que la última palabra no se corte según el comentario de ReX357.

No tengo suficientes puntos de representación para agregar esto como un comentario.

1
tanc

Se agregaron declaraciones IF/ELSEIF al código de Dave y AmalMurali para manejar cadenas sin espacios

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

Sé que esto es viejo, pero ...

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

Creo una función más similar a substr, y usando la idea de @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 .: El corte de longitud completa puede ser menor que el substr.

0
evandro777

Creo que esta es la forma más fácil de hacerlo:

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

Estoy usando los caracteres especiales para dividir el texto y cortarlo.

0
Namida

Usé esto 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

Encuentro que esto 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;

}

El búfer le permite ajustar la longitud de la cadena devuelta.

0
Mat Barnett

Utilizar esta:

el siguiente código eliminará ','. Si tiene cualquier otro carácter o subcadena, puede usar eso en lugar de ','

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

// si tienes otra cuenta de cadena para

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