在PHP中突出显示两个字符串之间差异的最简单方法是什么?
我想到的是Stack Overflow编辑历史页面的方式,其中新文本为绿色,删除的文本为红色。如果有任何预先编写的函数或类可用,那就太理想了。
刚刚编写了一个类来计算从一个字符串转换为另一个字符串所需的最小编辑次数(不应字面理解),详情请见:http://www.raymondhill.net/finediff/
它有一个静态函数来呈现差异的HTML版本。
这是第一个版本,可能会改进,但作为现在效果良好,因此我将其提供给需要有效地生成紧凑差异的人。
编辑:它现在在Github上:https://github.com/gorhill/PHP-FineDiff
您以前能够使用 PHP Horde_Text_Diff 软件包。
然而,该软件包现已不可用。
这是一个很好的算法,同时也有相关的PHP实现
解决这个问题并不像看起来那么简单,我花了一年时间烦恼着这个问题,最终用18行代码在PHP中完成了自己的算法。虽然这并不是最高效的diff方法,但它可能是最容易理解的。
它的工作原理是找到两个字符串共同拥有的最长单词序列,并递归地找到剩余字符串的最长序列,直到子字符串没有共同的单词为止。此时,将剩余的新单词作为插入,剩余的旧单词作为删除。
你可以在这里下载源代码:PHP SimpleDiff...
if($matrix[$oindex][$nindex] > $maxlen){
,maxlen
未定义。 - dynamic以下是一个简单的函数,可用于比较两个数组。它实现了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);
}
它生成两个数组:
如果您使用字符填充数组,则可以使用它来计算内联差异。现在只需要一步即可突出显示差异:
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;
}
Eg.:
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>
栈服务器故障
额外说明:
还有一个xdiff的PECL扩展:
特别地:
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;
}
我在使用基于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);
}
}
$sequence1 = $string1; $sequence2 = $string2; $end1 = strlen($string1) - 1; $end2 = strlen($string2) - 1;
替换为 $sequence1 = preg_split('//u', $string1, -1, PREG_SPLIT_NO_EMPTY); $sequence2 = preg_split('//u', $string2, -1, PREG_SPLIT_NO_EMPTY); $end1 = count($sequence1) - 1; $end2 = count($sequence2) - 1;
- GellweilerNeil Frasers的diff_match_patch的PHP版本(Apache 2.0许可证)
if (!function_exists()) {}
调用中包装mb_ord
和mb_char
函数(或删除它们),因为它们现在是内置函数,但它运行得很好。Apache 2.0许可证也非常宽松。 - undefined