Come troncare una stringa in PHP alla parola più vicina a un certo numero di caratteri?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Ho uno snippet di codice scritto in PHP che estrae un blocco di testo da un database e lo invia a un widget su una pagina web.Il blocco di testo originale può essere un lungo articolo o una o due frasi brevi;ma per questo widget non posso visualizzare più di, diciamo, 200 caratteri.Potrei usare substr() per tagliare il testo a 200 caratteri, ma il risultato sarebbe tagliare a metà delle parole-- quello che voglio veramente è tagliare il testo alla fine dell'ultimo parola prima di 200 caratteri.

È stato utile?

Soluzione

Utilizzando il wordwrap funzione.Divide il testo su più righe in modo tale che la larghezza massima sia quella specificata, interrompendo i confini delle parole.Dopo la divisione, prendi semplicemente la prima riga:

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

Una cosa che questo oneliner non gestisce è il caso in cui il testo stesso è più corto della larghezza desiderata.Per gestire questo caso limite, si dovrebbe fare qualcosa del tipo:

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

La soluzione di cui sopra presenta il problema di tagliare prematuramente il testo se contiene una nuova riga prima del punto di taglio effettivo.Ecco una versione che risolve questo 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));
}

Inoltre, ecco la classe test PHPUnit utilizzata per testare l'implementazione:

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

MODIFICARE :

I caratteri speciali UTF8 come "à" non vengono gestiti.Aggiungi "u" alla fine del REGEX per gestirlo:

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

Altri suggerimenti

Ciò restituirà i primi 200 caratteri di parole:

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

Ed ecco qua: un metodo affidabile per troncare qualsiasi stringa alla parola intera più vicina, rimanendo al di sotto della lunghezza massima della stringa.

Ho provato gli altri esempi sopra e non hanno prodotto i risultati desiderati.

La seguente soluzione è nata quando ho notato un parametro $break di wordwrap funzione:

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

Qui è la soluzione:

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

Esempio 1.

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

L'esempio precedente restituirà:

This is very long string...

Esempio n.2.

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

L'esempio precedente restituirà:

This is short string.

Tieni presente, ogni volta che dividi per "parola", che alcune lingue come il cinese e il giapponese non utilizzano il carattere spazio per dividere le parole.Inoltre, un utente malintenzionato potrebbe semplicemente inserire testo senza spazi o utilizzare un carattere Unicode simile al carattere di spazio standard, nel qual caso qualsiasi soluzione utilizzata potrebbe finire comunque per visualizzare l'intero testo.Un modo per aggirare questo problema potrebbe essere quello di controllare la lunghezza della stringa dopo averla divisa in spazi normalmente, quindi, se la stringa è ancora al di sopra di un limite anomalo - forse 225 caratteri in questo caso - andare avanti e dividerla stupidamente a quel limite.

Un altro avvertimento con cose come queste quando si tratta di caratteri non ASCII;le stringhe che li contengono possono essere interpretate dallo standard strlen() di PHP come più lunghe di quanto non siano in realtà, perché un singolo carattere può occupare due o più byte invece di uno solo.Se usi semplicemente le funzioni strlen()/substr() per dividere le stringhe, potresti dividere una stringa nel mezzo di un carattere!In caso di dubbio, mb_strlen()/mb_substr() sono un po' più infallibili.

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

Questo ti darà una stringa troncata al primo spazio dopo 30 caratteri.

Ecco la mia funzione basata sull'approccio di @Cd-MaN.

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

  return $string;
}

Ecco qui:

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

È sorprendente quanto sia difficile trovare la soluzione perfetta a questo problema.Non ho ancora trovato una risposta su questa pagina che non fallisca almeno in alcune situazioni (specialmente se la stringa contiene ritorni a capo o tabulazioni, o se la parola break è qualcosa di diverso da uno spazio, o se la stringa ha UTF- 8 caratteri multibyte).

Ecco una soluzione semplice che funziona in tutti i casi.C'erano risposte simili qui, ma il modificatore "s" è importante se si desidera che funzioni con input su più righe e il modificatore "u" consente di valutare correttamente i caratteri multibyte UTF-8.

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

Un possibile caso limite con questo...se la stringa non contiene spazi bianchi nei primi caratteri $characterCount, restituirà l'intera stringa.Se preferisci che forza un'interruzione in $characterCount anche se non è un limite di parola, puoi usare questo:

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

Un'ultima opzione, se vuoi che aggiunga i puntini di sospensione se tronca la stringa...

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

Descrizione:

  • ^ - inizia dall'inizio della stringa
  • ([\s\S]{1,200}) - ottieni da 1 a 200 di qualsiasi carattere
  • [\s]+? - Non includere spazi alla fine del breve testo in modo da evitare word ... invece di word...
  • [\s\S]+ - corrispondere a tutti gli altri contenuti

Test:

  1. regex101.com aggiungiamo a or pochi altri r
  2. regex101.com orrrr esattamente 200 caratteri.
  3. regex101.com dopo il quinto r orrrrr escluso.

Godere.

Per farlo utilizzerei la funzione preg_match, poiché quello che vuoi è un'espressione piuttosto semplice.

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

L'espressione significa "abbinare qualsiasi sottostringa a partire dall'inizio della lunghezza 1-200 che termina con uno spazio". Il risultato è nel risultato $ e la partita è in $ corrispondenze.Questo si prende cura della tua domanda originale, che termina specificamente in qualsiasi spazio.Se vuoi che finisca con un ritorno a capo, cambia l'espressione regolare in:

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

Ok, quindi ho ottenuto un'altra versione basata sulle risposte di cui sopra ma tenendo conto di più cose (utf-8, e  ), anche una riga che rimuove gli shortcode WordPress commentati se usati 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;
  }
/*
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);
}

Utilizzo:

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

Questo produrrà le prime 10 parole.

IL preg_split La funzione viene utilizzata per dividere una stringa in sottostringhe.I limiti lungo i quali la stringa deve essere divisa vengono specificati utilizzando un modello di espressione regolare.

preg_split La funzione accetta 4 parametri, ma solo i primi 3 sono rilevanti per noi in questo momento.

Primo parametro: pattern Il primo parametro è il modello di espressioni regolari lungo il quale la stringa deve essere divisa.Nel nostro caso, vogliamo dividere la stringa oltre i confini delle parole.Pertanto utilizziamo una classe di caratteri predefinita \s che corrisponde ai caratteri dello spazio bianco come spazio, tabulazione, ritorno a capo e avanzamento riga.

Secondo parametro: stringa di input Il secondo parametro è la stringa di testo lunga che vogliamo dividere.

Terzo parametro: limitare il terzo parametro specifica il numero di sottostringi che dovrebbero essere restituiti.Se imposti il ​​limite su n, preg_split restituirà un array di n elementi.Il primo n-1 gli elementi conterranno le sottostringhe.L'ultimo (n th) L'elemento conterrà il resto della stringa.

Basato sull'espressione regolare di @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]. '...';
}

Questa è una piccola correzione per la risposta di mattmac:

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

L'unica differenza è aggiungere uno spazio alla fine di $string.Ciò garantisce che l'ultima parola non venga tagliata come da commento di ReX357.

Non ho abbastanza punti reputazione per aggiungerlo come commento.

Ho una funzione che fa quasi quello che vuoi, se apporterai alcune modifiche, si adatterà esattamente:

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

Ecco come l'ho fatto:

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

So che è vecchio, ma...

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

L'ho usato prima

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

Creo una funzione più simile a substr e utilizzando l'idea di @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.:Il taglio a tutta lunghezza potrebbe essere inferiore alla sottostr.

Aggiunte istruzioni IF/ELSEIF al codice da Dave E AmalMurali per gestire stringhe senza spazi

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

Credo che questo sia il modo più semplice per farlo:

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

Sto usando i caratteri speciali per dividere il testo e tagliarlo.

Trovo che funzioni:

funzione abbrevia_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;

}

Il buffer consente di regolare la lunghezza della stringa restituita.

Usa questo:

il seguente codice rimuoverà ','.Se hai qualche altro carattere o sottostringa, puoi usarlo invece di ','

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

// se hai un altro account di stringa per

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

Qui puoi provare questo

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

Forse questo aiuterà qualcuno:

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

?>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top