199 votes

Comment tronquer une chaîne de caractères 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, disons, 200 caractères. Je pourrais utiliser substr() pour couper le texte à 200 caractères, mais le résultat serait une coupure au milieu des mots - ce que je veux vraiment, c'est couper le texte à la fin de la dernière phrase. mot avant 200 caractères.

2 votes

La question vise à dire que le texte tronqué tiendra dans un nombre fixe de pixels sur une page Web. Dans ce cas, selon la police choisie, l'espace requis par caractère n'est pas constant. Nous ne pouvons donc pas supposer que 200 caractères s'adapteront au mieux aux pixels disponibles. Jusqu'à présent (jusqu'au 02-Mar-2011), toutes les réponses ci-dessous manquent ce point et donc aucune d'entre elles ne fournit une solution fiable. - :(

1 votes

Non, pas vraiment. Vous pouvez définir la police de caractères de manière fiable, puis mesurer le pire des scénarios, c'est-à-dire le nombre de caractères les plus larges qui pourraient être insérés. Et si vous avez besoin d'être sûr à 100% de la façon dont le navigateur l'a rendu, ce n'est plus un problème PHP de toute façon.

0 votes

Essayez ce lien, il peut vous aider stackoverflow.com/a/26098951/3944217

237voto

Cd-MaN Points 7911

En utilisant le wordwrap fonction. Elle divise les textes en plusieurs lignes de manière à ce que la largeur maximale soit celle que vous avez spécifiée, avec une rupture aux limites des mots. Après la division, vous prenez simplement 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 où le texte lui-même est plus court que la largeur souhaitée. Pour gérer ce cas de figure, il faut faire quelque chose du genre :

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

La solution ci-dessus présente le problème de couper prématurément le texte s'il contient une nouvelle ligne avant le point de coupe 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));
}

Voici également 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));
  }
}

EDIT :

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

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

1 votes

Il semble que cela couperait prématurément le texte s'il y a un \n avant la largeur souhaitée.

0 votes

@KendallHopkins : vrai, il y a en effet un problème. J'ai mis à jour la réponse avec une implémentation alternative qui résout le problème donné.

0 votes

Cet exemple fonctionnerait-il pour une chaîne contenant des balises html, comme des balises de paragraphe ?

144voto

mattmac Points 1157

Cela renverra les 200 premiers caractères des mots :

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

8 votes

Presque. On dirait que ça enlève le dernier mot de la phrase pour moi, quoi qu'il arrive.

0 votes

Fonctionne très bien mais j'ai trouvé la même erreur que ReX357. Quand il y a plus d'un mot, il supprime le dernier.

26 votes

Il suffit de l'entourer d'une vérification pour s'assurer que la chaîne est plus longue que ce que vous testez (comme la réponse acceptée). if (strlen($string) > $your_desired_width) { preg_replace(...); }

50voto

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

Et voilà une méthode fiable pour tronquer une chaîne de caractères au mot entier le plus proche, tout en respectant la longueur maximale de la chaîne.

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

12 votes

Si la longueur de la chaîne donnée est inférieure à la longueur maximale, cela coupera tout jusqu'au dernier espace. Pour éviter cela, placez cette commande dans un fichier if déclaration : if (strlen($str) > 200) { ... }

0 votes

Simple et probablement beaucoup plus rapide que d'autres solutions.

2 votes

Un problème avec cette méthode est qu'elle renvoie une chaîne vide si la chaîne ne contient pas d'espace.

40voto

Sergiy Points 1552

La solution suivante a vu le jour lorsque j'ai remarqué que le paramètre $break de la commande wordwrap fonction :

string wordwrap ( string $str [, int $width = 75 [, string $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 n° 1.

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

L'exemple ci-dessus produira un résultat :

This is very long string...

Exemple n° 2.

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

L'exemple ci-dessus produira un résultat :

This is short string.

2 votes

Cela ne fonctionne pas si la chaîne de caractères contient déjà un caractère de nouvelle ligne (par exemple si vous essayez d'extraire un description d'un article de blog)

1 votes

@supersan On peut toujours prétraiter avec preg_replace('/\s+/', ' ', $description) pour remplacer tous les caractères d'espacement par un seul espace ;)

9voto

Garrett Albright Points 1703

N'oubliez pas que certaines langues, comme le chinois et le japonais, n'utilisent pas d'espace pour séparer les mots. De plus, un utilisateur malveillant pourrait simplement saisir du texte sans aucun espace, ou en utilisant un sosie Unicode du caractère espace standard, auquel cas toute solution que vous utilisez pourrait finir par afficher le texte entier de toute façon. Une façon de contourner ce problème pourrait être de vérifier la longueur de la chaîne après l'avoir divisée sur les 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 - de continuer et de la diviser bêtement à cette limite.

Il y a un autre problème avec les caractères non-ASCII ; les chaînes de caractères qui les contiennent peuvent être interprétées par la fonction strlen() de PHP comme étant plus longues qu'elles ne le sont réellement, car un seul caractère peut prendre deux octets ou plus au lieu d'un seul. Si vous utilisez uniquement les fonctions strlen()/substr() pour diviser les chaînes de caractères, 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X