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


183

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 breve frase o due; 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 nel mezzo di parole - quello che voglio davvero è tagliare il testo alla fine dell'ultima parola prima di 200 caratteri.


2
La domanda intende dire che il testo troncato si adatta a un numero fisso di pixel in una pagina Web. In questo caso, a seconda del carattere scelto, lo spazio richiesto per carattere non è costante. E quindi non possiamo supporre che 200 caratteri si adattino meglio ai pixel disponibili. Finora (fino al 02-mar-2011), tutte le risposte sottostanti mancano di questo punto e quindi nessuna di esse fornisce una soluzione affidabile. - :(
LionHeart

1
No, non proprio. Puoi impostare il carattere in modo affidabile e quindi misurare lo scenario peggiore, ad esempio il numero di caratteri più ampi, e se devi essere sicuro al 100% del modo in cui il browser lo ha reso, non è più un problema PHP.
Mołot,


Potresti trovare s($str)->truncateSafely(200)utile, come si trova in questa libreria autonoma .
Caw

Risposte:


221

Usando la funzione wordwrap . Suddivide i testi in più righe in modo tale che la larghezza massima sia quella specificata, spezzando 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 sopra ha 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 di 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 UTF8 speciali 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);


1
Sembra che taglierebbe prematuramente il testo se esiste un valore \nprecedente alla larghezza desiderata.
Kendall Hopkins,

@KendallHopkins: vero, c'è davvero un problema. Ho aggiornato la risposta con un'implementazione alternativa che risolve il problema dato.
Pantera grigia il

Questo esempio funzionerebbe con una stringa che contiene tag html come tag di paragrafo?
limitlessloop

è davvero utile per me, il mio mal di testa era Arabiclettere lunghe e si è ridotto a correggere le parole ora con l'aiuto della tokenTruncatefunzione .. tnx un milione :)
Aditya P Bhatt

1
Perché non aggiungere: if (strlen ($ string) <= $ your_desired_width) restituisce $ string; come prima affermazione?
Darko Romanov,

139

Ciò restituirà i primi 200 caratteri di parole:

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

7
Quasi. Sembra che rimuova l'ultima parola della frase per me, non importa quale.
ReX357,

funziona benissimo ma ho riscontrato lo stesso errore di ReX357. Quando c'è più di 1 parola, cancella l'ultima.
Andres SK,

25
Basta avvolgerlo in un segno di spunta per assicurarsi che la stringa sia più lunga di quella per cui si sta testando (uguale alla risposta accettata)if (strlen($string) > $your_desired_width) { preg_replace(...); }
Blair McMillan,

Ho modificato la risposta per includere i consigli di @BlairMcMillan
Kim Stacks il

2
Piccolo miglioramento alla regex: le parentesi rendono il \ S + finale opzionale per la partita, ma catturano anche quei personaggi. Dato che non abbiamo bisogno di catturare quei personaggi, rendi le parentesi non catturate in questo modo:/\s+?(?:\S+)?$/
pcronin

45
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

E il gioco è fatto: un metodo affidabile per troncare qualsiasi stringa all'intera parola più vicina, rimanendo al di sotto della lunghezza massima della stringa.

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


11
Se la lunghezza della stringa data è inferiore alla lunghezza massima, ciò taglierebbe tutto fino all'ultimo spazio. Per evitarlo, racchiudilo in una ifdichiarazione:if (strlen($str) > 200) { ... }
Amal Murali,

Semplice e probabilmente molto più veloce di altre soluzioni.
Vladan,

1
Un problema con questo è che restituisce una stringa vuota se la stringa non contiene uno spazio.
or

Può essere semplificato a:$WidgetText = substr($string, 0, strpos($string, ' ', 200));
wranvaud,

36

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

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

Ecco 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 sopra mostrerà:

This is very long string...

Esempio n. 2.

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

L'esempio sopra mostrerà:

This is short string.

2
questo non funziona se la stringa ha già un nuovo carattere di linea (ad esempio se stai cercando di estrarre un descriptionpost di blog)
Sostituisci l'

1
@supersan Può sempre pre-elaborare con preg_replace('/\s+/', ' ', $description)per sostituire tutti i caratteri degli spazi bianchi con un singolo spazio;)
Mavelo,

9

Tieni a mente ogni volta che stai dividendo per "parola" ovunque che alcune lingue come cinese e giapponese non utilizzino un carattere spaziale per dividere le parole. Inoltre, un utente malintenzionato può semplicemente inserire il testo senza spazi o utilizzare un Unicode simile al carattere dello spazio standard, nel qual caso qualsiasi soluzione che si utilizza può finire per visualizzare l'intero testo comunque. Un modo per aggirare questo può essere quello di controllare la lunghezza della stringa dopo averla suddivisa su spazi normalmente, quindi, se la stringa è ancora al di sopra di un limite anomalo - forse 225 caratteri in questo caso - andando avanti e dividendola stupidamente a quel limite.

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


8

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 nel primo spazio dopo 30 caratteri.


1
Salve, se la lunghezza della stringa senza spazio sarà inferiore a 30, verrà restituito un errore. e qui il risultato sarà di primi 31 caratteri e non di 30 ..
Er. Anurag Jain,

5

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

Grazie, ho trovato la tua funzione più utile e affidabile di tutte queste risposte per le mie esigenze. Tuttavia, come posso farlo supportare stringhe multi-byte?
ctrlbrk

5

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

4
$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 personaggio
  • [\s]+?- non includere spazi alla fine del breve testo in modo che possiamo evitare word ...invece diword...
  • [\s\S]+ - abbina tutti gli altri contenuti

test:

  1. regex101.comaggiungiamo a orpochi altrir
  2. regex101.com orrrr esattamente 200 caratteri.
  3. regex101.comdopo il quinto r orrrrrescluso.

Godere.


non capisco la documentazione di PHP. so che $1è una "sostituzione", ma in questo specifico contesto a cosa si riferisce ?? una variabile vuota?
oldboy

1
$1Riferimenti @Anthony per abbinare parentesi quadre ([\s\S]{1,200}). $2farà riferimento a due secondi di parentesi quadre se ce ne sono nel modello.
hlcs,

3

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

Ecco una soluzione semplice che funziona in tutti i casi. Ci sono state risposte simili qui, ma il modificatore "s" è importante se vuoi che funzioni con l'input multi-linea e il modificatore "u" lo fa 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 ha alcuno spazio nei primi $ characterCount caratteri, restituirà l'intera stringa. Se preferisci, impone un'interruzione a $ characterCount anche se non è un limite di parole, 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;
}

2

Vorrei usare la funzione preg_match per farlo, poiché ciò che vuoi è un'espressione piuttosto semplice.

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

L'espressione significa "abbina qualsiasi sottostringa a partire dall'inizio della lunghezza 1-200 che termina con uno spazio". Il risultato è in $ risultato e la partita è in $ partite. Questo si occupa della tua domanda originale, che sta finendo specificamente in qualsiasi spazio. Se vuoi farlo finire su newline, cambia l'espressione regolare in:

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

2

Ok, quindi ho ottenuto un'altra versione di questo sulla base delle risposte di cui sopra, ma tenendo conto di più cose (utf-8, \ n e & nbsp;), anche una riga che rimuove i codici brevi di wordpress commentati se usata 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

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. Questo assicura che l'ultima parola non sia tagliata secondo il commento di ReX357.

Non ho abbastanza punti rep per aggiungere questo come commento.


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

Questo produrrà le prime 10 parole.

La preg_splitfunzione viene utilizzata per dividere una stringa in sottostringhe. I confini lungo i quali deve essere divisa la stringa, sono specificati usando un modello di espressioni regolari.

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 pattern di espressioni regolari lungo il quale deve essere suddivisa la stringa. Nel nostro caso, vogliamo dividere la stringa tra i limiti delle parole. Pertanto utilizziamo una classe di caratteri predefinita \sche corrisponde a caratteri di spazi bianchi 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 - Limite Il terzo parametro specifica il numero di sottostringhe che devono essere restituite. Se si imposta il limite su n, preg_split restituirà una matrice di n elementi. I primi n-1elementi conterranno le sottostringhe. L'ultimo (n th)elemento conterrà il resto della stringa.


1

Basato sul regex 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]. '...';
}

1

Ho una funzione che fa quasi quello che vuoi, se farai 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;
}
?>

1

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

0

So che questo è vecchio, ma ...

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

0

Creo una funzione più simile a substr, e usando 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 integrale può essere inferiore al substr.


0

Aggiunte istruzioni IF / ELSEIF al codice di Dave e AmalMurali per la gestione delle 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);
}

0

Trovo che funzioni:

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

}

Il buffer consente di regolare la lunghezza della stringa restituita.


0

Usa questo:

il seguente codice rimuoverà ','. Se hai altri caratteri o sotto-stringhe, puoi usarlo al posto di ","

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

// se hai un altro account stringa per

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

0

Sebbene questa sia una domanda piuttosto vecchia, ho pensato che avrei fornito un'alternativa, poiché non era menzionata e valida per PHP 4.3+.

È possibile utilizzare la sprintffamiglia di funzioni per troncare il testo, utilizzando il %.ℕsmodificatore di precisione.

Un punto .seguito da un numero intero il cui significato dipende dallo specificatore:

  • Per gli identificatori e, E, f e F: questo è il numero di cifre da stampare dopo il punto decimale (per impostazione predefinita, è 6).
  • Per gli identificatori g e G: questo è il numero massimo di cifre significative da stampare.
  • Per lo specificatore: funge da punto di interruzione, impostando un limite massimo di caratteri per la stringa

Troncamento semplice https://3v4l.org/QJDJU

$string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var_dump(sprintf('%.10s', $string));

Risultato

string(10) "0123456789"

Troncamento espanso https://3v4l.org/FCD21

Dal momento che sprintffunziona in modo simile substre taglierà parzialmente le parole. L'approccio seguente assicurerà che le parole non vengano troncate utilizzando strpos(wordwrap(..., '[break]'), '[break]')un delimitatore speciale. Questo ci consente di recuperare la posizione e di garantire che non corrispondiamo alle strutture di frasi standard.

Restituisce una stringa senza tagliare parzialmente le parole e che non supera la larghezza specificata, preservando le interruzioni di riga se lo si desidera.

function truncate($string, $width, $on = '[break]') {
    if (strlen($string) > $width && false !== ($p = strpos(wordwrap($string, $width, $on), $on))) {
        $string = sprintf('%.'. $p . 's', $string);
    }
    return $string;
}
var_dump(truncate('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', 20));

var_dump(truncate("Lorem Ipsum is simply dummy text of the printing and typesetting industry.", 20));

var_dump(truncate("Lorem Ipsum\nis simply dummy text of the printing and typesetting industry.", 20));

Risultato

/* 
string(36) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"  
string(14) "Lorem Ipsum is" 
string(14) "Lorem Ipsum
is" 
*/

Risultati usando wordwrap($string, $width)ostrtok(wordwrap($string, $width), "\n")

/*
string(14) "Lorem Ipsum is"
string(11) "Lorem Ipsum"
*/

-1

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

-1

Qui puoi provare questo

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

Tale soluzione era già stata menzionata in altre risposte. Il problema è che non riesce se la stringa ha una lunghezza inferiore a 200 caratteri o se non contiene spazi. Inoltre non limita la stringa a 200 caratteri, invece rompe la stringa nello spazio dopo 200 caratteri, che di solito non è quello che vuoi.
or

-1

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.


-2

Può essere 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;
    }

?>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.