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


J'ai un extrait de code écrit en PHP qui extrait 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 200 caractères, par exemple. Je pourrais utiliser substr () pour couper le texte à 200 caractères, mais le résultat serait coupé au milieu des mots - ce que je veux vraiment, c'est couper le texte à la fin du dernier mot avant 200 caractères.


162
2017-09-17 04:24


origine


Réponses:


En utilisant le un mot fonction. Il divise les textes en plusieurs lignes, de telle sorte que la largeur maximale soit celle que vous avez spécifiée, en fractionnant aux limites des mots. Après le fractionnement, vous prenez simplement la première ligne:

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

Une chose que ne manie pas cet oneliner est le cas où le texte lui-même est plus court que la largeur souhaitée. Pour gérer cet avantage, il faut 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 pose le problème de couper prématurément le texte s'il contient une nouvelle ligne avant le point de coupure réel. 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));
}

En outre, voici la classe de test PHPUnit utilisée pour tester l'implémentation:

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

MODIFIER :

Les caractères UTF8 spéciaux tels que 'à' ne sont pas gérés. Ajoutez 'u' à la fin du REGEX pour le gérer:

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


208
2017-09-17 04:27



Cela retournera les 200 premiers caractères des mots:

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

121
2017-09-17 04:41



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

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

J'ai essayé les autres exemples ci-dessus et ils n'ont pas produit les résultats souhaités.


41
2018-01-12 04:29



La solution suivante est née lorsque j'ai remarqué un paramètre $ break de un mot fonction:

string wordwrap (chaîne $ str [, int $ width = 75 [, chaîne $ break =   "\ n" [, bool $ cut = 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);

L'exemple ci-dessus va afficher:

This is very long string...

Exemple # 2.

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

L'exemple ci-dessus va afficher:

This is short string.

30
2017-07-25 08:10



Gardez à l'esprit que chaque fois que vous divisez par «mot» n'importe où, certaines langues telles que le chinois et le japonais n'utilisent pas de caractère d'espace pour séparer les mots. En outre, un utilisateur malveillant peut simplement saisir du texte sans espaces, ou utiliser un squelette Unicode dans le caractère d'espace standard, auquel cas toute solution que vous utilisez peut finir par afficher le texte dans son intégralité. Un moyen de contourner cela peut être de vérifier la longueur de la chaîne après l'avoir scindée en espaces normalement, alors si la chaîne est toujours au-dessus d'une limite anormale - peut-être 225 caractères dans ce cas -.

Une autre mise en garde concernant des choses comme celles-ci en ce qui concerne les caractères non-ASCII; Les chaînes contenant ces chaînes peuvent être interprétées comme étant plus longues que ce qu’elles sont réellement, car un seul caractère peut prendre deux octets ou plus au lieu d’un seul. Si vous utilisez simplement les fonctions strlen () / substr () pour fractionner des chaînes, vous pouvez diviser une chaîne au milieu d'un caractère! En cas de doute, mb_strlen ()/mb_substr () sont un peu plus infaillibles.


9
2017-09-17 06:08



Utilisez 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
2017-09-17 04:29



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
2018-03-26 12:36



Voici:

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
2017-09-17 04:31



Il est surprenant de voir à 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 au moins dans certaines situations (surtout si la chaîne contient des nouvelles lignes ou des tabulations, ou si le mot pause est autre chose qu'un espace, ou si la chaîne a UTF- 8 caractères multi-octets).

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 qu'il fonctionne avec une entrée multi-lignes, 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 d'arête 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 coupure à $ characterCount même si ce n'est pas une limite de mot, 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 qu'il ajoute des points de suspension s'il 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
2017-09-01 20:55