Выделите разницу между двумя строками в переполнении стека

Какой самый простой способ выделить разницу между двумя строками в PHP?

Я думаю о том, что происходит на странице истории редактирования переполнения стека, где новый текст выделен зеленым, а удаленный — красным. Если есть какие-либо заранее написанные функции или классы, это было бы идеально.

130

Решение

Вы можете использовать PHP Horde_Text_Diff пакет. Он соответствует вашим потребностям, а также вполне настраиваемый.

Он также лицензирован по лицензии GPL, так что наслаждайтесь!

42

Другие решения

Просто написал класс для вычисления наименьшего (не считаться буквально) количества правок для преобразования одной строки в другую строку:

http://www.raymondhill.net/finediff/

Он имеет статическую функцию для отображения HTML-версии diff.

Это первая версия, которая, вероятно, будет улучшена, но на данный момент она работает просто отлично, поэтому я добавлю ее на тот случай, если кому-то понадобится эффективно сгенерировать компакт-диск, как мне и нужно.

Изменить: сейчас на Github:
https://github.com/gorhill/PHP-FineDiff

71

Если вы хотите надежную библиотеку, Text_Diff (пакет PEAR) выглядит довольно хорошо. У него есть довольно интересные функции.

25

Это также хороший
http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

Решение проблемы не так просто, как кажется, и проблема беспокоила меня около года, прежде чем я понял это. Мне удалось написать свой алгоритм на PHP, в 18 строк кода. Это не самый эффективный способ проведения различий, но, вероятно, его проще всего понять.

Он работает, находя самую длинную последовательность слов, общих для обеих строк, и рекурсивно находит самые длинные последовательности остатков строки, пока подстроки не имеют общих слов. На этом этапе он добавляет оставшиеся новые слова в качестве вставки и оставшиеся старые слова в качестве удаления.

Вы можете скачать исходный код здесь: PHP SimpleDiff

23

Вот короткая функция, которую вы можете использовать для сравнения двух массивов. Он реализует 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);
}

Генерирует два массива:

  • массив значений: список элементов в том виде, в котором они отображаются в diff.
  • Маска-массив: содержит цифры. 0: без изменений, -1: удалено, 1: добавлено.

Если вы заполняете массив символами, он может использоваться для вычисления встроенной разницы. Теперь просто один шаг, чтобы выделить различия:

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

Например.:

echo diffline('StackOverflow', 'ServerFault')

Будет выводить:

S<del>tackO</del><ins>er</ins>ver<del>f</del><ins>Fau</ins>l<del>ow</del><ins>t</ins>

STackoерверееФолвлT

Дополнительные примечания:

  • Для матрицы diff требуется (m + 1) * (n + 1) элементов. Таким образом, вы можете столкнуться с ошибками нехватки памяти, если попытаетесь различить длинные последовательности. В этом случае сначала разберитесь с большими кусками (например, строками), а затем разложите их содержимое во втором проходе.
  • Алгоритм может быть улучшен, если вы урежете совпадающие элементы от начала и до конца, а затем запустите алгоритм только на отличающейся середине. последняя (более раздутая) версия содержит эти модификации тоже.
12

Существует также расширение PECL для xdiff:

Особенно:

Пример из руководства по 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;
}
6

Это лучший, который я нашел.

http://code.stephenmorley.org/php/diff-implementation/

введите описание изображения здесь

4

У меня были ужасные проблемы с показанными на основе PEAR и более простыми альтернативами. Итак, вот решение, которое использует команду Unix diff (очевидно, вы должны быть в системе Unix или иметь работающую команду Windows diff для ее работы). Выберите ваш любимый временный каталог и измените исключения на коды возврата, если хотите.

/**
* @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);
}
}
4
По вопросам рекламы [email protected]