在PHP中突出显示两个字符串之间差异的最简单方法是什么?
我正在考虑Stack Overflow编辑历史记录页面,其中新文本为绿色,删除的文本为红色.如果有任何预先编写的函数或类可用,那将是理想的.
刚写了一个类来计算最小的(不是字面意思)编辑数,将一个字符串转换成另一个字符串:
http://www.raymondhill.net/finediff/
它有一个静态函数来呈现diff的HTML版本.
这是第一个版本,可能会有所改进,但它现在工作得很好,所以我把它扔出去,以防有人需要有效地生成一个紧凑的差异,就像我需要的那样.
编辑:现在在Github上:https: //github.com/gorhill/PHP-FineDiff
您可以使用PHP Horde_Text_Diff包.它适合您的需求,也可以定制.
它也是根据GPL许可的,所以尽情享受!
如果你想要一个健壮的库,Text_Diff(一个PEAR包)看起来相当不错.它有一些非常酷的功能.
这是一个很好的,也是 http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/
解决这个问题并不像看起来那么简单,在我弄清楚问题之前,这个问题困扰了我一年左右.我设法用PHP编写我的算法,用18行代码编写.它不是最有效的差异方法,但它可能是最容易理解的.
它的工作原理是找到两个字符串共有的最长的单词序列,并递归地找到字符串剩余部分的最长序列,直到子字符串没有相同的单词.此时,它将剩余的新单词添加为插入,将剩余的旧单词添加为删除.
你可以在这里下载源代码:PHP SimpleDiff ...
这是一个可用于区分两个数组的简短函数.它实现了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); }
它生成两个数组:
values数组: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 .= ''; break; case 1: $result .= ''; break; } switch ($mc) { case -1: $result .= ''; break; case 1: $result .= ''; break; } } $result .= $diffval[$i]; $pmc = $mc; } switch ($pmc) { case -1: $result .= ''; break; case 1: $result .= ''; break; } return $result; }
例如.:
echo diffline('StackOverflow', 'ServerFault')
将输出:
StackOerverfFaulowt
小号tackOerverF福勒流Ť
补充说明:
diff矩阵需要(m + 1)*(n + 1)个元素.因此,如果您尝试区分长序列,则可能会遇到内存不足错误.在这种情况下,首先区分较大的块(例如,线),然后在第二遍中区分它们的内容.
如果从开头和结尾修剪匹配元素,则可以改进算法,然后仅在不同的中间运行算法.一个后(更臃肿)版本包含这些修改过.
xdiff还有一个PECL扩展:
http://php.net/manual/en/book.xdiff.php
http://pecl.php.net/package/xdiff
特别是:
xdiff_string_diff - 制作两个字符串的统一差异
PHP手册示例:
7> xgretsch..:对于基于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); } }