Quel est le moyen le plus simple de mettre en évidence la différence entre deux chaînes en PHP?
Je pense à la page d’historique d’édition de Stack Overflow, où le nouveau texte est en vert et le texte supprimé en rouge. S'il y a des fonctions ou des classes pré-écrites disponibles, ce serait l'idéal.
Vous pouvez utiliser le package PHP Horde_Text_Diff . Cela répond à vos besoins et est tout à fait personnalisable.
Il est également sous licence GPL, alors profitez-en!
Vient d’écrire une classe pour calculer le plus petit nombre (à ne pas prendre littéralement) de modifications pour transformer une chaîne en une autre:
http://www.raymondhill.net/finediff/
Il a une fonction statique pour rendre une version HTML du diff.
C'est une première version, qui devrait être améliorée, mais elle fonctionne très bien dès maintenant. Je la lance donc au cas où quelqu'un aurait besoin de générer un diff compact de manière efficace, comme j'avais besoin.
Edit: C’est maintenant sur Github: https://github.com/gorhill/PHP-FineDiff
Si vous voulez une bibliothèque robuste, Text_Diff (un paquet PEAR) a l’air d’être plutôt bon. Il a quelques fonctionnalités assez cool.
C’est aussi un bel exemple http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/
Résoudre le problème n’est pas aussi simple qu’il semble, et le problème m’a dérangé pendant environ un an avant que je ne le voie. J'ai réussi à écrire mon algorithme en PHP, en 18 lignes de code. Ce n'est pas le moyen le plus efficace de faire un diff, mais c'est probablement le plus facile à comprendre.
Cela fonctionne en recherchant la plus longue séquence de mots commune aux deux chaînes et en recherchant récursivement les plus longues séquences des restes de la chaîne jusqu'à ce que les sous-chaînes n'aient plus de mots en commun. À ce stade, il ajoute les nouveaux mots restants sous forme d'insertion et les anciens mots restants sous forme de suppression.
Vous pouvez télécharger la source ici: PHP SimpleDiff ...
Voici une courte fonction que vous pouvez utiliser pour différencier deux tableaux. Il implémente l'algorithme LCS :
function computeDiff($from, $to)
{
$diffValues = array();
$diffMask = array();
$dm = array();
$n1 = count($from);
$n2 = count($to);
for ($j = -1; $j < $n2; $j++) $dm[-1][$j] = 0;
for ($i = -1; $i < $n1; $i++) $dm[$i][-1] = 0;
for ($i = 0; $i < $n1; $i++)
{
for ($j = 0; $j < $n2; $j++)
{
if ($from[$i] == $to[$j])
{
$ad = $dm[$i - 1][$j - 1];
$dm[$i][$j] = $ad + 1;
}
else
{
$a1 = $dm[$i - 1][$j];
$a2 = $dm[$i][$j - 1];
$dm[$i][$j] = max($a1, $a2);
}
}
}
$i = $n1 - 1;
$j = $n2 - 1;
while (($i > -1) || ($j > -1))
{
if ($j > -1)
{
if ($dm[$i][$j - 1] == $dm[$i][$j])
{
$diffValues[] = $to[$j];
$diffMask[] = 1;
$j--;
continue;
}
}
if ($i > -1)
{
if ($dm[$i - 1][$j] == $dm[$i][$j])
{
$diffValues[] = $from[$i];
$diffMask[] = -1;
$i--;
continue;
}
}
{
$diffValues[] = $from[$i];
$diffMask[] = 0;
$i--;
$j--;
}
}
$diffValues = array_reverse($diffValues);
$diffMask = array_reverse($diffMask);
return array('values' => $diffValues, 'mask' => $diffMask);
}
Il génère deux tableaux:
Si vous remplissez un tableau avec des caractères, vous pouvez l'utiliser pour calculer la différence en ligne. Maintenant, juste une étape pour souligner les différences:
function diffline($line1, $line2)
{
$diff = computeDiff(str_split($line1), str_split($line2));
$diffval = $diff['values'];
$diffmask = $diff['mask'];
$n = count($diffval);
$pmc = 0;
$result = '';
for ($i = 0; $i < $n; $i++)
{
$mc = $diffmask[$i];
if ($mc != $pmc)
{
switch ($pmc)
{
case -1: $result .= '</del>'; break;
case 1: $result .= '</ins>'; break;
}
switch ($mc)
{
case -1: $result .= '<del>'; break;
case 1: $result .= '<ins>'; break;
}
}
$result .= $diffval[$i];
$pmc = $mc;
}
switch ($pmc)
{
case -1: $result .= '</del>'; break;
case 1: $result .= '</ins>'; break;
}
return $result;
}
Par exemple.:
echo diffline('StackOverflow', 'ServerFault')
Est-ce que la sortie:
S<del>tackO</del><ins>er</ins>ver<del>f</del><ins>Fau</ins>l<del>ow</del><ins>t</ins>
StackOerverfFaulowt
Notes complémentaires:
Il existe également une extension PECL pour xdiff:
En particulier:
Exemple tiré du manuel PHP:
<?php
$old_article = file_get_contents('./old_article.txt');
$new_article = $_POST['article'];
$diff = xdiff_string_diff($old_article, $new_article, 1);
if (is_string($diff)) {
echo "Differences between two articles:\n";
echo $diff;
}
C'est le meilleur que j'ai trouvé.
J'ai eu de gros problèmes avec les alternatives présentées, à la fois simples et basées sur PEAR. Voici donc une solution qui exploite la commande Unix diff (vous devez évidemment être sur un système Unix ou disposer d’une commande Windows diff qui fonctionne pour que cela fonctionne). Choisissez votre répertoire temporaire favori et modifiez les exceptions pour renvoyer les codes si vous préférez.
/**
* @brief Find the difference between two strings, lines assumed to be separated by "\n|
* @param $new string The new string
* @param $old string The old string
* @return string Human-readable output as produced by the Unix diff command,
* or "No changes" if the strings are the same.
* @throws Exception
*/
public static function diff($new, $old) {
$tempdir = '/var/somewhere/tmp'; // Your favourite temporary directory
$oldfile = tempnam($tempdir,'OLD');
$newfile = tempnam($tempdir,'NEW');
if (!@file_put_contents($oldfile,$old)) {
throw new Exception('diff failed to write temporary file: ' .
print_r(error_get_last(),true));
}
if (!@file_put_contents($newfile,$new)) {
throw new Exception('diff failed to write temporary file: ' .
print_r(error_get_last(),true));
}
$answer = array();
$cmd = "diff $newfile $oldfile";
exec($cmd, $answer, $retcode);
unlink($newfile);
unlink($oldfile);
if ($retcode != 1) {
throw new Exception('diff failed with return code ' . $retcode);
}
if (empty($answer)) {
return 'No changes';
} else {
return implode("\n", $answer);
}
}
Ce que vous recherchez est un "algorithme de diff". Une recherche rapide sur Google m'a conduit à cette solution . Je ne l'ai pas testé, mais peut-être qu'il fera ce dont vous avez besoin.
Un port php de Neil Frasers diff_match_patch (Apache 2.0 sous licence)
Je recommanderais de regarder ces super fonctions du noyau PHP:
similar_text - Calcule la similarité entre deux chaînes
http://www.php.net/manual/en/function.similar-text.php
levenshtein - Calcule la distance de Levenshtein entre deux chaînes
http://www.php.net/manual/en/function.levenshtein.php
soundex - Calcule la clé soundex d'une chaîne
http://www.php.net/manual/en/function.soundex.php
metaphone - Calcule la clé de métaphone d'une chaîne
Je suis tombé sur cette classe PHP diff de Chris Boulton basée sur Python difflib qui pourrait être une bonne solution: