Comment tronquer une chaîne de caractères en PHP vers le mot le plus proche d'un certain nombre de caractères?

j'ai un extrait de code écrit en PHP qui tire un bloc de texte d'une base de données et l'envoie à un widget sur une page web. Le bloc de texte original peut être un long article ou une courte phrase ou deux; mais pour ce widget Je ne peux pas afficher plus de, disons, 200 caractères. Je pourrais utiliser substr() pour couper le texte à 200 caractères, mais le résultat serait de couper au milieu des mots-- ce que je veux vraiment c'est couper le texte à la fin du dernier mot avant de 200 caractères.

165
demandé sur Brian 2008-09-17 08:24:04

25 réponses

en utilisant la fonction wordwrap . Il divise les textes en plusieurs lignes de telle sorte que la largeur maximale est celle que vous avez spécifiée, en brisant les limites des mots. Après la séparation, il vous suffit de prendre la première ligne:

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

une chose que cet oneliner ne gère pas est le cas lorsque le texte lui-même est plus court que la largeur désirée. Pour gérer ce bord-case, on devrait faire quelque chose comme:

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

la solution ci-dessus a le problème de couper prématurément le texte s'il contient une nouvelle ligne avant la ligne de coupure réelle. Voici une version qui résout ce problème:

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

aussi, voici la classe de test PHPUnit utilisée pour tester la mise en œuvre:

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 :

les caractères spéciaux UTF8 comme " à " ne sont pas manipulés. Ajouter "u" à la fin de la REGEX à manipuler it:

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

210
répondu Grey Panther 2015-09-09 20:32:10

cette option renvoie les 200 premiers caractères des mots:

preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, 201));
121
répondu mattmac 2008-09-17 04:41:34
$WidgetText = substr($string, 0, strrpos(substr($string, 0, 200), ' '));

et là vous l'avez - une méthode fiable pour tronquer n'importe quelle corde au mot entier le plus proche, tout en restant sous la longueur maximale de corde.

j'ai essayé d'autres exemples ci-dessus et elles n'ont pas produit les résultats escomptés.

42
répondu Dave 2011-01-12 04:29:50

la solution suivante est née quand j'ai remarqué un paramètre $ break de wordwrap fonction:

chaîne wordwrap ( string $str [, int $largeur = 75 [, string $break = "\n" [, bool $taille = false ]]] )

Voici la solution :

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

exemple #1.

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

le l'exemple ci-dessus produira:

This is very long string...

exemple #2.

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

l'exemple ci-dessus produira:

This is short string.
32
répondu Sergiy Sokolenko 2014-08-20 13:19:35

gardez à l'esprit chaque fois que vous vous séparez par" mot " n'importe où que certaines langues comme le chinois et le japonais n'utilisent pas un caractère d'espace pour séparer les mots. En outre, un utilisateur malveillant pourrait simplement entrer du texte sans espaces, ou en utilisant un sosie Unicode au caractère d'espace standard, auquel cas toute solution que vous utilisez peut finir par afficher le texte entier de toute façon. Un moyen de contourner cela peut être de vérifier la longueur de la corde après le fractionnement sur des espaces comme d'habitude, puis, si la chaîne est toujours au-dessus d'une limite anormale - peut - être 225 caractères dans ce cas-ci-aller de l'avant et le diviser dumbly à cette limite.

une mise en garde de plus avec des choses comme ça quand il s'agit de caractères non-ASCII; les chaînes qui les contiennent peuvent être interprétées par le standard de PHP strlen() comme étant plus longues qu'elles ne le sont réellement, parce qu'un seul caractère peut prendre deux ou plusieurs octets au lieu d'un seul. Si vous utilisez simplement les fonctions strlen()/substr() pour séparer les chaînes, vous pouvez séparer une chaîne en au milieu d'un personnage! En cas de doute, mb_strlen() / mb_substr() sont un peu plus infaillible.

9
répondu Garrett Albright 2013-12-06 18:18:36

utiliser strpos et 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;

cela vous donnera une chaîne tronquée au premier espace après 30 caractères.

8
répondu Lucas Oman 2011-03-03 19:28:54

voici ma fonction basée sur l'approche 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
répondu Camsoft 2010-03-26 12:36:03

Ici, vous allez:

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
répondu UnkwnTech 2008-09-17 04:31:26

il est surprenant à quel point il est difficile de trouver la solution parfaite à ce problème. Je n'ai pas encore trouvé de réponse sur cette page qui n'échoue pas dans certaines situations (surtout si la chaîne contient des lignes nouvelles ou des onglets, ou si le mot break est autre chose qu'un espace, ou si la chaîne a des caractères multi-octets UTF-8).

Voici une solution simple qui fonctionne dans tous les cas. Il y avait des réponses similaires ici, mais le modificateur "s" est important si vous voulez il fonctionne avec une entrée multi-ligne, et le modificateur "u" lui permet d'évaluer correctement les caractères multi-octets UTF-8.

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

un cas de bord possible avec ceci... si la chaîne n'a aucun espace dans les premiers caractères $characterCount, elle retournera la chaîne entière. Si vous préférez qu'il force une pause à $ characterCount même si ce n'est pas une limite de mots, vous pouvez utiliser ceci:

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

Une dernière option, si vous voulez l'avoir ajouter ellipsis si elle tronque la chaîne...

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
répondu orrd 2015-09-01 21:04:36
$shorttext = preg_replace('/^([\s\S]{1,200})[\s]+?[\s\S]+/', '', $fulltext);

Description:

  • ^ - début de la chaîne
  • ([\s\S]{1,200}) - obtenez de 1 à 200 de n'importe quel caractère
  • [\s]+? - ne pas inclure d'espaces à la fin du court texte de sorte que nous pouvons éviter word ... au lieu de word...
  • [\s\S]+ - correspond à tous les autres contenus

Essais:

  1. regex101.com ajoutons à or quelques autres r
  2. regex101.com orrrr exactement 200 caractères.
  3. regex101.com après cinquième r orrrrr exclu.

de Profiter de.

3
répondu hlcs 2016-11-04 17:06:08

j'utiliserais la fonction preg_match pour faire ceci, car ce que vous voulez est une expression assez simple.

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

l'expression signifie" correspondre à tout substrat commençant à partir du début de la longueur 1-200 qui se termine par un espace."Le résultat est en $result, et le match est en $matches. Cela répond à votre question originale, qui se termine précisément sur n'importe quel espace. Si vous voulez qu'il se termine sur newlines, changez l'expression régulière en:

$result = preg_match("/^(.{1,199})[\n]/i", $text, $matches);
2
répondu Justin Poliey 2008-09-17 04:59:08

Ok donc j'ai eu une autre version de celui-ci basé sur les réponses ci-dessus, mais en prenant plus de choses en compte(utf-8, \N et  ), aussi une ligne de stripping les shortcodes wordpress commenté si utilisé avec 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
répondu Yo-L 2011-10-28 10:52:18
/*
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);
}

Utilisation:

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

ceci produira les 10 premiers mots.

la fonction preg_split est utilisée pour séparer une chaîne en substrats. Les limites le long desquelles la chaîne doit être fendue, sont spécifiées en utilisant un motif d'expressions régulières.

preg_split fonction prend 4 paramètres, mais seulement les 3 premiers sont pertinents pour nous maintenant.

Premier Paramètre – Motif Première le paramètre est le motif des expressions régulières le long duquel la chaîne doit être divisée. Dans notre cas, nous voulons partager la chaîne au-delà des frontières des mots. Par conséquent, nous utilisons une classe de caractères prédéfinis \s , qui correspond à caractères blancs (espace, tabulation, retour chariot et saut de ligne.

Second Paramètre-Chaîne D'Entrée Le second paramètre est la longue chaîne de texte que nous voulons diviser.

Troisième Paramètre-Limite Le troisième paramètre spécifie le nombre de sous-couches à retourner. Si vous définissez la limite de n , preg_split retournera un tableau de n éléments. Les premiers éléments n-1 contiendront les substrats. Le dernier élément (n th) contiendra le reste de la chaîne.

2
répondu bodi0 2012-04-05 09:32:56

basé sur le 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
répondu amateur barista 2010-12-09 16:28:08

c'est une petite correction pour la réponse de mattmac:

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

la seule différence est d'ajouter un espace à la fin de $string. Cela garantit que le dernier mot n'est pas coupé selon le commentaire de ReX357.

Je n'ai pas assez de répliques pour ajouter ceci comme commentaire.

1
répondu tanc 2011-11-09 22:29:11

j'ai une fonction qui fait presque ce que vous voulez, si vous faites quelques modifications, elle conviendra exactement:

<?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
répondu Rikudou_Sennin 2014-06-13 11:37:05

C'est comme ça que je l'ai fait:

$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
répondu Shashank Saxena 2016-02-18 12:20:43

je sais que c'est vieux, mais...

function _truncate($str, $limit) {
    if(strlen($str) < $limit)
        return $str;
    $uid = uniqid();
    return array_shift(explode($uid, wordwrap($str, $limit, $uid)));
}
0
répondu gosukiwi 2013-02-26 12:53:52

j'ai utilisé ça avant

<?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
répondu Yousef Altaf 2014-07-08 11:31:18

je crée une fonction plus similaire à substr, et en utilisant l'idée 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.: La longueur totale de la coupe peut être inférieure à substr.

0
répondu evandro777 2015-06-25 14:27:18

a ajouté les déclarations IF/ELSEIF au code de Dave et AmalMurali pour manipuler des cordes sans espaces

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
répondu jdorenbush 2017-05-23 10:31:37

je crois que c'est la façon la plus facile de le faire:

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

j'utilise les caractères spéciaux pour séparer le texte et le couper.

0
répondu Namida 2018-03-09 13:26:34

je trouve que cela fonctionne:

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

}

le buffer vous permet de régler la longueur de la chaîne retournée.

0
répondu Mat Barnett 2018-05-11 11:00:25

ici vous pouvez essayer ceci

substr( $str, 0, strpos($str, ' ', 200) ); 
-1
répondu Abhijeet kumar sharma 2015-08-26 12:46:05

peut-être cela aidera quelqu'un:

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

?>
-2
répondu slash3b 2014-04-01 14:31:24